From 09c7d158d1180dc9ec042b67f8c1ecb1015a142e Mon Sep 17 00:00:00 2001 From: obscuren Date: Fri, 22 Aug 2014 12:12:41 +0200 Subject: [PATCH 01/39] Error window --- README.md | 2 +- ethereal/errors.go | 36 ++++++++++++++++++++++++++++++++++++ ethereal/main.go | 20 +++++++++++++++++++- ethereum/main.go | 2 +- utils/cmd.go | 11 ++++++++++- 5 files changed, 67 insertions(+), 4 deletions(-) create mode 100644 ethereal/errors.go diff --git a/README.md b/README.md index 2d4b128fc963..8cdcfe2a6d2d 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.4. +Current state: Proof of Concept 0.6.5. For the development package please see the [eth-go package](https://github.com/ethereum/eth-go). diff --git a/ethereal/errors.go b/ethereal/errors.go new file mode 100644 index 000000000000..409b7a281455 --- /dev/null +++ b/ethereal/errors.go @@ -0,0 +1,36 @@ +package main + +import ( + "fmt" + "os" + + "gopkg.in/qml.v1" +) + +func ErrorWindow(err error) { + engine := qml.NewEngine() + component, e := engine.LoadString("local", qmlErr) + if e != nil { + fmt.Println("err:", err) + os.Exit(1) + } + + win := component.CreateWindow(nil) + win.Root().ObjectByName("label").Set("text", err.Error()) + win.Show() + win.Wait() +} + +const qmlErr = ` +import QtQuick 2.0; import QtQuick.Controls 1.0; +ApplicationWindow { + width: 600; height: 150; + flags: Qt.CustomizeWindowHint | Qt.WindowTitleHint | Qt.WindowCloseButtonHint + title: "Error" + Text { + x: parent.width / 2 - this.width / 2; + y: parent.height / 2 - this.height / 2; + objectName: "label"; + } +} +` diff --git a/ethereal/main.go b/ethereal/main.go index 4cb8630e80da..3729dbdaff69 100644 --- a/ethereal/main.go +++ b/ethereal/main.go @@ -1,6 +1,7 @@ package main import ( + "fmt" "os" "runtime" @@ -12,7 +13,7 @@ import ( const ( ClientIdentifier = "Ethereal" - Version = "0.6.4" + Version = "0.6.5" ) var ethereum *eth.Ethereum @@ -28,6 +29,23 @@ func run() error { utils.InitLogging(Datadir, LogFile, LogLevel, DebugFile) db := utils.NewDatabase() + err := utils.DBSanityCheck(db) + if err != nil { + engine := qml.NewEngine() + component, e := engine.LoadString("local", qmlErr) + if e != nil { + fmt.Println("err:", err) + os.Exit(1) + } + + win := component.CreateWindow(nil) + win.Root().ObjectByName("label").Set("text", err.Error()) + win.Show() + win.Wait() + + ErrorWindow(err) + os.Exit(1) + } keyManager := utils.NewKeyManager(KeyStore, Datadir, db) diff --git a/ethereum/main.go b/ethereum/main.go index b7c8ea1e77fd..937a2b399fe8 100644 --- a/ethereum/main.go +++ b/ethereum/main.go @@ -13,7 +13,7 @@ import ( const ( ClientIdentifier = "Ethereum(G)" - Version = "0.6.4" + Version = "0.6.5" ) var logger = ethlog.NewLogger("CLI") diff --git a/utils/cmd.go b/utils/cmd.go index cda735c27d6e..3f3948d60315 100644 --- a/utils/cmd.go +++ b/utils/cmd.go @@ -80,6 +80,16 @@ func confirm(message string) bool { return r == "y" } +func DBSanityCheck(db ethutil.Database) error { + d, _ := db.Get([]byte("ProtocolVersion")) + protov := ethutil.NewValue(d).Uint() + if protov != eth.ProtocolVersion && protov != 0 { + return fmt.Errorf("Database version mismatch. Protocol(%d / %d). `rm -rf %s`", protov, eth.ProtocolVersion, ethutil.Config.ExecPath+"/database") + } + + return nil +} + func InitDataDir(Datadir string) { _, err := os.Stat(Datadir) if err != nil { @@ -112,7 +122,6 @@ func InitConfig(ConfigFile string, Datadir string, EnvPrefix string) *ethutil.Co func exit(err error) { status := 0 if err != nil { - fmt.Println(err) logger.Errorln("Fatal: ", err) status = 1 } From 77fd361c62ea84a761aedead906cdd9f38ac79eb Mon Sep 17 00:00:00 2001 From: obscuren Date: Fri, 22 Aug 2014 12:12:53 +0200 Subject: [PATCH 02/39] Error window --- ethereal/main.go | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/ethereal/main.go b/ethereal/main.go index 3729dbdaff69..7b157f3dbf51 100644 --- a/ethereal/main.go +++ b/ethereal/main.go @@ -1,7 +1,6 @@ package main import ( - "fmt" "os" "runtime" @@ -31,18 +30,6 @@ func run() error { db := utils.NewDatabase() err := utils.DBSanityCheck(db) if err != nil { - engine := qml.NewEngine() - component, e := engine.LoadString("local", qmlErr) - if e != nil { - fmt.Println("err:", err) - os.Exit(1) - } - - win := component.CreateWindow(nil) - win.Root().ObjectByName("label").Set("text", err.Error()) - win.Show() - win.Wait() - ErrorWindow(err) os.Exit(1) } From 842f2cc8a06627a1de4fbdd1580fefe14be85a04 Mon Sep 17 00:00:00 2001 From: obscuren Date: Fri, 22 Aug 2014 12:14:37 +0200 Subject: [PATCH 03/39] Error window --- ethereal/main.go | 1 + ethereum/main.go | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/ethereal/main.go b/ethereal/main.go index 7b157f3dbf51..dd4f1245afe9 100644 --- a/ethereal/main.go +++ b/ethereal/main.go @@ -31,6 +31,7 @@ func run() error { err := utils.DBSanityCheck(db) if err != nil { ErrorWindow(err) + os.Exit(1) } diff --git a/ethereum/main.go b/ethereum/main.go index 937a2b399fe8..9d07ab0d41b7 100644 --- a/ethereum/main.go +++ b/ethereum/main.go @@ -40,6 +40,11 @@ func main() { utils.InitLogging(Datadir, LogFile, LogLevel, DebugFile) db := utils.NewDatabase() + err := utils.DBSanityCheck(db) + if err != nil { + logger.Errorln(err) + os.Exit(1) + } keyManager := utils.NewKeyManager(KeyStore, Datadir, db) From 5ac875b097a55c75a7e3da3443ef9fe64fc87846 Mon Sep 17 00:00:00 2001 From: obscuren Date: Fri, 22 Aug 2014 12:40:15 +0200 Subject: [PATCH 04/39] Set log level of std logger as well. (since gui logging is disabled) --- ethereal/gui.go | 4 +++- ethereal/main.go | 3 ++- ethereum/main.go | 1 + utils/cmd.go | 8 ++++++-- 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/ethereal/gui.go b/ethereal/gui.go index 6d16ec484d12..5e87f7464d2d 100644 --- a/ethereal/gui.go +++ b/ethereal/gui.go @@ -56,7 +56,8 @@ type Gui struct { plugins map[string]plugin - miner *ethminer.Miner + miner *ethminer.Miner + stdLog ethlog.LogSystem } // Create GUI, but doesn't start it @@ -559,6 +560,7 @@ func (gui *Gui) ToggleTurboMining() { // functions that allow Gui to implement interface ethlog.LogSystem func (gui *Gui) SetLogLevel(level ethlog.LogLevel) { gui.logLevel = level + gui.stdLog.SetLogLevel(level) gui.config.Save("loglevel", level) } diff --git a/ethereal/main.go b/ethereal/main.go index dd4f1245afe9..4fb9e2a9c9f4 100644 --- a/ethereal/main.go +++ b/ethereal/main.go @@ -25,7 +25,7 @@ func run() error { utils.InitDataDir(Datadir) - utils.InitLogging(Datadir, LogFile, LogLevel, DebugFile) + stdLog := utils.InitLogging(Datadir, LogFile, LogLevel, DebugFile) db := utils.NewDatabase() err := utils.DBSanityCheck(db) @@ -53,6 +53,7 @@ func run() error { } gui := NewWindow(ethereum, config, clientIdentity, KeyRing, LogLevel) + gui.stdLog = stdLog utils.RegisterInterrupt(func(os.Signal) { gui.Stop() diff --git a/ethereum/main.go b/ethereum/main.go index 9d07ab0d41b7..ab6ce18b21c9 100644 --- a/ethereum/main.go +++ b/ethereum/main.go @@ -43,6 +43,7 @@ func main() { err := utils.DBSanityCheck(db) if err != nil { logger.Errorln(err) + os.Exit(1) } diff --git a/utils/cmd.go b/utils/cmd.go index 3f3948d60315..83f3ec0b6846 100644 --- a/utils/cmd.go +++ b/utils/cmd.go @@ -100,18 +100,22 @@ func InitDataDir(Datadir string) { } } -func InitLogging(Datadir string, LogFile string, LogLevel int, DebugFile string) { +func InitLogging(Datadir string, LogFile string, LogLevel int, DebugFile string) ethlog.LogSystem { var writer io.Writer if LogFile == "" { writer = os.Stdout } else { writer = openLogFile(Datadir, LogFile) } - ethlog.AddLogSystem(ethlog.NewStdLogSystem(writer, log.LstdFlags, ethlog.LogLevel(LogLevel))) + + sys := ethlog.NewStdLogSystem(writer, log.LstdFlags, ethlog.LogLevel(LogLevel)) + ethlog.AddLogSystem(sys) if DebugFile != "" { writer = openLogFile(Datadir, DebugFile) ethlog.AddLogSystem(ethlog.NewStdLogSystem(writer, log.LstdFlags, ethlog.DebugLevel)) } + + return sys } func InitConfig(ConfigFile string, Datadir string, EnvPrefix string) *ethutil.ConfigManager { From ded013b7a7367f74b9c854755a7ce4a6955e93c0 Mon Sep 17 00:00:00 2001 From: obscuren Date: Sat, 23 Aug 2014 11:00:15 +0200 Subject: [PATCH 05/39] Minor updates to the UI --- ethereal/assets/qml/views/info.qml | 130 ++++++++++++++++++++--------- ethereal/gui.go | 28 +++++++ ethereal/main.go | 1 - 3 files changed, 117 insertions(+), 42 deletions(-) diff --git a/ethereal/assets/qml/views/info.qml b/ethereal/assets/qml/views/info.qml index ca6ca077e0d3..9ac2a1a0996e 100644 --- a/ethereal/assets/qml/views/info.qml +++ b/ethereal/assets/qml/views/info.qml @@ -44,59 +44,104 @@ Rectangle { gui.setCustomIdentifier(text) } } - } - property var addressModel: ListModel { - id: addressModel + TextArea { + objectName: "statsPane" + width: parent.width + height: 200 + selectByMouse: true + readOnly: true + font.family: "Courier" + } } - TableView { - id: addressView + + RowLayout { + id: logLayout width: parent.width height: 200 - anchors.bottom: logLayout.top - TableViewColumn{ role: "name"; title: "name" } - 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() + anchors.bottom: parent.bottom + + TableView { + id: addressView + width: parent.width + height: 200 + anchors { + left: parent.left + right: logLevelSlider.left + bottom: parent.bottom + top: parent.top + } + TableViewColumn{ role: "name"; title: "name" } + TableViewColumn{ role: "address"; title: "address"; width: 300} + + property var addressModel: ListModel { + id: addressModel + } + + 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) + } + } + } } - Menu { - id: contextMenu - property var row; + Slider { + id: logLevelSlider + value: gui.getLogLevelInt() + anchors { + right: parent.right + top: parent.top + bottom: parent.bottom - MenuItem { - text: "Copy" - onTriggered: { - copyToClipboard(addressModel.get(this.row).address) - } + rightMargin: 5 + leftMargin: 5 + topMargin: 5 + bottomMargin: 5 + } + + orientation: Qt.Vertical + maximumValue: 5 + stepSize: 1 + + onValueChanged: { + gui.setLogLevel(value) } } } @@ -104,6 +149,8 @@ Rectangle { property var logModel: ListModel { id: logModel } + + /* RowLayout { id: logLayout width: parent.width @@ -147,6 +194,7 @@ Rectangle { } } } + */ function addDebugMessage(message){ debuggerLog.append({value: message}) diff --git a/ethereal/gui.go b/ethereal/gui.go index 5e87f7464d2d..4849fbab7f2d 100644 --- a/ethereal/gui.go +++ b/ethereal/gui.go @@ -6,6 +6,7 @@ import ( "fmt" "math/big" "os" + "runtime" "strconv" "strings" "time" @@ -412,6 +413,7 @@ func (gui *Gui) update() { peerUpdateTicker := time.NewTicker(5 * time.Second) generalUpdateTicker := time.NewTicker(1 * time.Second) + statsUpdateTicker := time.NewTicker(5 * time.Second) state := gui.eth.StateManager().TransState() @@ -488,6 +490,10 @@ func (gui *Gui) update() { pow := gui.miner.GetPow() miningLabel.Set("text", "Mining @ "+strconv.FormatInt(pow.GetHashrate(), 10)+"Khash") } + + case <-statsUpdateTicker.C: + gui.setStatsPane() + } } }() @@ -507,6 +513,28 @@ func (gui *Gui) update() { reactor.Subscribe("peerList", peerChan) } +func (gui *Gui) setStatsPane() { + var memStats runtime.MemStats + runtime.ReadMemStats(&memStats) + + statsPane := gui.getObjectByName("statsPane") + statsPane.Set("text", fmt.Sprintf(`###### Ethereal 0.6.4 (%s) ####### + +CPU: # %d +Goroutines: # %d +CGoCalls: # %d + +Alloc: %d +Heap Alloc: %d + +CGNext: %x +NumGC: %d +`, runtime.Version(), runtime.NumCPU, runtime.NumGoroutine(), runtime.NumCgoCall(), + memStats.Alloc, memStats.HeapAlloc, + memStats.NextGC, memStats.NumGC, + )) +} + func (gui *Gui) CopyToClipboard(data string) { //clipboard.WriteAll("test") fmt.Println("COPY currently BUGGED. Here are the contents:\n", data) diff --git a/ethereal/main.go b/ethereal/main.go index 4fb9e2a9c9f4..ac94a66229a5 100644 --- a/ethereal/main.go +++ b/ethereal/main.go @@ -71,7 +71,6 @@ func main() { // 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 From 444c9effdb2458d7130be5a5e1cad1d719c67114 Mon Sep 17 00:00:00 2001 From: obscuren Date: Sat, 23 Aug 2014 15:30:23 +0200 Subject: [PATCH 06/39] Check data length --- ethereal/assets/qml/views/info.qml | 1 - ethereal/assets/qml/wallet.qml | 1 - ethereal/ui_lib.go | 2 +- 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/ethereal/assets/qml/views/info.qml b/ethereal/assets/qml/views/info.qml index 9ac2a1a0996e..8a1d4d84ae47 100644 --- a/ethereal/assets/qml/views/info.qml +++ b/ethereal/assets/qml/views/info.qml @@ -106,7 +106,6 @@ Rectangle { } } } - } Menu { diff --git a/ethereal/assets/qml/wallet.qml b/ethereal/assets/qml/wallet.qml index 90cc42a1fa45..ed527ced7c78 100644 --- a/ethereal/assets/qml/wallet.qml +++ b/ethereal/assets/qml/wallet.qml @@ -43,7 +43,6 @@ ApplicationWindow { // Takes care of loading all default plugins Component.onCompleted: { 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"}) diff --git a/ethereal/ui_lib.go b/ethereal/ui_lib.go index 4b8210da64c4..fa09751e12a2 100644 --- a/ethereal/ui_lib.go +++ b/ethereal/ui_lib.go @@ -53,7 +53,7 @@ func (self *UiLib) LookupDomain(domain string) string { data := world.Config().Get("DnsReg").StorageString(domain).Bytes() // Left padded = A record, Right padded = CNAME - if data[0] == 0 { + if len(data) > 0 && data[0] == 0 { data = bytes.TrimLeft(data, "\x00") var ipSlice []string for _, d := range data { From 4be75b185845c959ffbfdad5bf263ad520c64d27 Mon Sep 17 00:00:00 2001 From: obscuren Date: Sat, 23 Aug 2014 15:42:58 +0200 Subject: [PATCH 07/39] moved methods --- ethereal/gui.go | 145 ------------------------------------------------ 1 file changed, 145 deletions(-) diff --git a/ethereal/gui.go b/ethereal/gui.go index 4849fbab7f2d..1fb6c05e6820 100644 --- a/ethereal/gui.go +++ b/ethereal/gui.go @@ -5,7 +5,6 @@ import ( "encoding/json" "fmt" "math/big" - "os" "runtime" "strconv" "strings" @@ -20,17 +19,11 @@ import ( "github.com/ethereum/eth-go/ethreact" "github.com/ethereum/eth-go/ethutil" "github.com/ethereum/eth-go/ethwire" - "github.com/ethereum/go-ethereum/utils" "gopkg.in/qml.v1" ) 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 @@ -147,24 +140,6 @@ func (gui *Gui) Stop() { logger.Infoln("Stopped") } -func (gui *Gui) ToggleMining() { - var txt string - if gui.eth.Mining { - utils.StopMining(gui.eth) - txt = "Start mining" - - gui.getObjectByName("miningLabel").Set("visible", false) - } else { - utils.StartMining(gui.eth) - gui.miner = utils.GetMiner() - txt = "Stop mining" - - gui.getObjectByName("miningLabel").Set("visible", true) - } - - gui.win.Root().Set("miningButtonText", txt) -} - func (gui *Gui) showWallet(context *qml.Context) (*qml.Window, error) { component, err := gui.engine.LoadFile(gui.uiLib.AssetPath("qml/wallet.qml")) if err != nil { @@ -178,44 +153,9 @@ 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 - } func (gui *Gui) ImportKey(filePath string) { @@ -535,11 +475,6 @@ NumGC: %d )) } -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)) @@ -556,83 +491,3 @@ func (gui *Gui) privateKey() string { func (gui *Gui) address() []byte { return gui.eth.KeyManager().Address() } - -func (gui *Gui) Transact(recipient, value, gas, gasPrice, d string) (*ethpipe.JSReceipt, error) { - 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) -} - -func (gui *Gui) SetCustomIdentifier(customIdentifier string) { - gui.clientIdentity.SetCustomIdentifier(customIdentifier) - gui.config.Save("id", customIdentifier) -} - -func (gui *Gui) GetCustomIdentifier() string { - return gui.clientIdentity.GetCustomIdentifier() -} - -func (gui *Gui) ToggleTurboMining() { - gui.miner.ToggleTurbo() -} - -// functions that allow Gui to implement interface ethlog.LogSystem -func (gui *Gui) SetLogLevel(level ethlog.LogLevel) { - gui.logLevel = level - gui.stdLog.SetLogLevel(level) - gui.config.Save("loglevel", level) -} - -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 { - return int(gui.logLevel) -} - -func (gui *Gui) Println(v ...interface{}) { - gui.printLog(fmt.Sprintln(v...)) -} - -func (gui *Gui) Printf(format string, v ...interface{}) { - gui.printLog(fmt.Sprintf(format, v...)) -} - -// Print function that logs directly to the GUI -func (gui *Gui) printLog(s string) { - /* - str := strings.TrimRight(s, "\n") - lines := strings.Split(str, "\n") - - view := gui.getObjectByName("infoView") - for _, line := range lines { - view.Call("addLog", line) - } - */ -} From 997e92191de5a7eeca2d9759c7b2aa0ee6b8aab9 Mon Sep 17 00:00:00 2001 From: obscuren Date: Sat, 23 Aug 2014 15:43:16 +0200 Subject: [PATCH 08/39] Moved files --- ethereal/bindings.go | 148 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 148 insertions(+) create mode 100644 ethereal/bindings.go diff --git a/ethereal/bindings.go b/ethereal/bindings.go new file mode 100644 index 000000000000..e129ea6aff57 --- /dev/null +++ b/ethereal/bindings.go @@ -0,0 +1,148 @@ +package main + +import ( + "encoding/json" + "fmt" + "os" + "strconv" + + "github.com/ethereum/eth-go/ethchain" + "github.com/ethereum/eth-go/ethlog" + "github.com/ethereum/eth-go/ethpipe" + "github.com/ethereum/eth-go/ethutil" + "github.com/ethereum/go-ethereum/utils" +) + +type plugin struct { + Name string `json:"name"` + Path string `json:"path"` +} + +func (gui *Gui) Println(v ...interface{}) { + gui.printLog(fmt.Sprintln(v...)) +} + +func (gui *Gui) Printf(format string, v ...interface{}) { + gui.printLog(fmt.Sprintf(format, v...)) +} + +// Print function that logs directly to the GUI +func (gui *Gui) printLog(s string) { + /* + str := strings.TrimRight(s, "\n") + lines := strings.Split(str, "\n") + + view := gui.getObjectByName("infoView") + for _, line := range lines { + view.Call("addLog", line) + } + */ +} +func (gui *Gui) Transact(recipient, value, gas, gasPrice, d string) (*ethpipe.JSReceipt, error) { + 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) +} + +func (gui *Gui) SetCustomIdentifier(customIdentifier string) { + gui.clientIdentity.SetCustomIdentifier(customIdentifier) + gui.config.Save("id", customIdentifier) +} + +func (gui *Gui) GetCustomIdentifier() string { + return gui.clientIdentity.GetCustomIdentifier() +} + +func (gui *Gui) ToggleTurboMining() { + gui.miner.ToggleTurbo() +} + +// functions that allow Gui to implement interface ethlog.LogSystem +func (gui *Gui) SetLogLevel(level ethlog.LogLevel) { + gui.logLevel = level + gui.stdLog.SetLogLevel(level) + gui.config.Save("loglevel", level) +} + +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 { + return int(gui.logLevel) +} +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) +} +func (gui *Gui) ToggleMining() { + var txt string + if gui.eth.Mining { + utils.StopMining(gui.eth) + txt = "Start mining" + + gui.getObjectByName("miningLabel").Set("visible", false) + } else { + utils.StartMining(gui.eth) + gui.miner = utils.GetMiner() + txt = "Stop mining" + + gui.getObjectByName("miningLabel").Set("visible", true) + } + + gui.win.Root().Set("miningButtonText", txt) +} From e68c502f7a0a3affffe889e3453151cdd1ef451e Mon Sep 17 00:00:00 2001 From: obscuren Date: Mon, 25 Aug 2014 12:53:17 +0200 Subject: [PATCH 09/39] Display block size --- ethereal/assets/qml/views/chain.qml | 7 +++---- ethereal/gui.go | 6 +++--- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/ethereal/assets/qml/views/chain.qml b/ethereal/assets/qml/views/chain.qml index 9eaa49db1801..ed019acf158a 100644 --- a/ethereal/assets/qml/views/chain.qml +++ b/ethereal/assets/qml/views/chain.qml @@ -10,7 +10,6 @@ Rectangle { id: root property var title: "Network" property var iconSource: "../net.png" - property var secondary: "Hi" property var menuItem objectName: "chainView" @@ -110,9 +109,9 @@ Rectangle { } 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)}) + blockModel.append({size: block.size, 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)}) + blockModel.insert(0, {size: block.size, 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; @@ -137,7 +136,7 @@ Rectangle { anchors.top: parent.top anchors.left: parent.left Text { text: '

Block details

'; color: "#F2F2F2"} - Text { text: 'Block number: ' + number; color: "#F2F2F2"} + Text { text: 'Block number: ' + number + " (Size: " + size + ")"; color: "#F2F2F2"} Text { text: 'Hash: ' + hash; color: "#F2F2F2"} Text { text: 'Coinbase: <' + name + '> ' + coinbase; color: "#F2F2F2"} Text { text: 'Block found at: ' + prettyTime; color: "#F2F2F2"} diff --git a/ethereal/gui.go b/ethereal/gui.go index 1fb6c05e6820..bd1466e8b754 100644 --- a/ethereal/gui.go +++ b/ethereal/gui.go @@ -279,7 +279,7 @@ func (gui *Gui) insertTransaction(window string, tx *ethchain.Transaction) { ptx.Address = r if window == "post" { - gui.getObjectByName("transactionView").Call("addTx", ptx, inout) + //gui.getObjectByName("transactionView").Call("addTx", ptx, inout) } else { gui.getObjectByName("pendingTxView").Call("addTx", ptx, inout) } @@ -393,12 +393,12 @@ func (gui *Gui) update() { if bytes.Compare(tx.Sender(), gui.address()) == 0 { object.SubAmount(tx.Value) - gui.getObjectByName("transactionView").Call("addTx", ethpipe.NewJSTx(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", ethpipe.NewJSTx(tx), "recv") + //gui.getObjectByName("transactionView").Call("addTx", ethpipe.NewJSTx(tx), "recv") gui.txDb.Put(tx.Hash(), tx.RlpEncode()) } From 1cdf0a2c514993db6d2cd27c647260585af61ed9 Mon Sep 17 00:00:00 2001 From: obscuren Date: Mon, 25 Aug 2014 13:02:20 +0200 Subject: [PATCH 10/39] Any address instead of my own --- ethereal/assets/qml/views/wallet.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ethereal/assets/qml/views/wallet.qml b/ethereal/assets/qml/views/wallet.qml index 5e10a70224f7..4628aef38d4e 100644 --- a/ethereal/assets/qml/views/wallet.qml +++ b/ethereal/assets/qml/views/wallet.qml @@ -151,7 +151,7 @@ Rectangle { model: ListModel { id: txModel Component.onCompleted: { - var messages = JSON.parse(eth.messages({latest: -1, from: "e6716f9544a56c530d868e4bfbacb172315bdead"})) + var messages = JSON.parse(eth.messages({latest: -1, from: eth.key().address})) 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)}) From 4e6defd6570dd213c53d73035e235431bb5408b5 Mon Sep 17 00:00:00 2001 From: obscuren Date: Mon, 25 Aug 2014 13:13:46 +0200 Subject: [PATCH 11/39] Add txs as they come in --- ethereal/assets/qml/views/wallet.qml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/ethereal/assets/qml/views/wallet.qml b/ethereal/assets/qml/views/wallet.qml index 4628aef38d4e..ce472812e4e5 100644 --- a/ethereal/assets/qml/views/wallet.qml +++ b/ethereal/assets/qml/views/wallet.qml @@ -5,6 +5,7 @@ import QtQuick.Dialogs 1.0; import QtQuick.Window 2.1; import QtQuick.Controls.Styles 1.1 import Ethereum 1.0 +import "../../ext/filter.js" as Eth Rectangle { id: root @@ -151,10 +152,15 @@ Rectangle { model: ListModel { id: txModel Component.onCompleted: { - var messages = JSON.parse(eth.messages({latest: -1, from: eth.key().address})) + var filter = new Eth.Filter({latest: -1, from: eth.key().address}) + filter.changed(addTxs) + + addTxs(filter.messages()) + } + function addTxs(messages) { 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)}) + txModel.insert(0, {num: txModel.count, from: message.from, to: message.to, value: eth.numberToHuman(message.value)}) } } } From 893e9256a0f48b8fd45f29717145a4df23a3a799 Mon Sep 17 00:00:00 2001 From: obscuren Date: Mon, 8 Sep 2014 00:50:25 +0200 Subject: [PATCH 12/39] Some minor corrections --- ethereal/assets/ext/filter.js | 2 +- ethereal/assets/qml/views/chain.qml | 11 ++++++---- ethereal/assets/qml/views/wallet.qml | 1 + ethereal/assets/qml/wallet.qml | 31 +++++++++++++++------------- ethereal/bindings.go | 2 +- ethereal/gui.go | 10 ++++----- ethereal/ui_lib.go | 4 ++++ 7 files changed, 35 insertions(+), 26 deletions(-) diff --git a/ethereal/assets/ext/filter.js b/ethereal/assets/ext/filter.js index 5c1c03aada48..6d6ec8748e22 100644 --- a/ethereal/assets/ext/filter.js +++ b/ethereal/assets/ext/filter.js @@ -3,7 +3,7 @@ var Filter = function(options) { this.seed = Math.floor(Math.random() * 1000000); this.options = options; - if(options == "chain") { + if(options === "chain") { eth.registerFilterString(options, this.seed); } else if(typeof options === "object") { eth.registerFilter(options, this.seed); diff --git a/ethereal/assets/qml/views/chain.qml b/ethereal/assets/qml/views/chain.qml index ed019acf158a..5bfc4b6c7da0 100644 --- a/ethereal/assets/qml/views/chain.qml +++ b/ethereal/assets/qml/views/chain.qml @@ -98,15 +98,18 @@ Rectangle { function addBlock(block, initial) { - var txs = JSON.parse(block.transactions); - var amount = 0 if(initial == undefined){ initial = false } + /* + var txs = JSON.parse(block.transactions); if(txs != null){ amount = txs.length } + */ + var txs = block.transactions; + var amount = block.transactions.length; if(initial){ blockModel.append({size: block.size, 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)}) @@ -241,8 +244,8 @@ Rectangle { singleBlock.set(0,block) popup.height = 300 transactionModel.clear() - if(block.txs != undefined){ - for(var i = 0; i < block.txs.count; ++i) { + if(block.txs !== undefined){ + for(var i = 0; i < block.txs.length; i++) { transactionModel.insert(0, block.txs.get(i)) } if(block.txs.get(0).data){ diff --git a/ethereal/assets/qml/views/wallet.qml b/ethereal/assets/qml/views/wallet.qml index ce472812e4e5..c2513289ef52 100644 --- a/ethereal/assets/qml/views/wallet.qml +++ b/ethereal/assets/qml/views/wallet.qml @@ -157,6 +157,7 @@ Rectangle { addTxs(filter.messages()) } + function addTxs(messages) { for(var i = 0; i < messages.length; i++) { var message = messages[i]; diff --git a/ethereal/assets/qml/wallet.qml b/ethereal/assets/qml/wallet.qml index ed527ced7c78..0ddbd26bd235 100644 --- a/ethereal/assets/qml/wallet.qml +++ b/ethereal/assets/qml/wallet.qml @@ -18,7 +18,7 @@ ApplicationWindow { height: 600 minimumHeight: 300 - title: "Ether browser" + title: "Ethegate" // This signal is used by the filter API. The filter API connects using this signal handler from // the different QML files and plugins. @@ -55,25 +55,28 @@ ApplicationWindow { } function addPlugin(path, options) { - var component = Qt.createComponent(path); - if(component.status != Component.Ready) { - if(component.status == Component.Error) { - console.debug("Error:"+ component.errorString()); + try { + var component = Qt.createComponent(path); + if(component.status != Component.Ready) { + if(component.status == Component.Error) { + console.debug("Error:"+ component.errorString()); + } + + return } - return - } + var views = mainSplit.addComponent(component, options) + views.menuItem.path = path - var views = mainSplit.addComponent(component, options) - views.menuItem.path = path + mainSplit.views.push(views); - mainSplit.views.push(views); + if(!options.noAdd) { + gui.addPlugin(path) + } - if(!options.noAdd) { - gui.addPlugin(path) + return views.view + } catch(e) { } - - return views.view } MenuBar { diff --git a/ethereal/bindings.go b/ethereal/bindings.go index e129ea6aff57..141c4a469fb7 100644 --- a/ethereal/bindings.go +++ b/ethereal/bindings.go @@ -78,7 +78,7 @@ func (gui *Gui) GetLogLevel() ethlog.LogLevel { } func (self *Gui) AddPlugin(pluginPath string) { - self.plugins[pluginPath] = plugin{Name: "SomeName", Path: pluginPath} + self.plugins[pluginPath] = plugin{Name: pluginPath, Path: pluginPath} json, _ := json.MarshalIndent(self.plugins, "", " ") ethutil.WriteFile(ethutil.Config.ExecPath+"/plugins.json", json) diff --git a/ethereal/gui.go b/ethereal/gui.go index bd1466e8b754..33fd35e6e559 100644 --- a/ethereal/gui.go +++ b/ethereal/gui.go @@ -5,6 +5,7 @@ import ( "encoding/json" "fmt" "math/big" + "path" "runtime" "strconv" "strings" @@ -63,12 +64,7 @@ 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("plugins:", string(data)) - + data, _ := ethutil.ReadAllFile(path.Join(ethutil.Config.ExecPath, "plugins.json")) json.Unmarshal([]byte(data), &gui.plugins) return gui @@ -339,6 +335,8 @@ func (gui *Gui) update() { }() for _, plugin := range gui.plugins { + logger.Infoln("Loading plugin ", plugin.Name) + gui.win.Root().Call("addPlugin", plugin.Path, "") } diff --git a/ethereal/ui_lib.go b/ethereal/ui_lib.go index fa09751e12a2..80e711aa7bf7 100644 --- a/ethereal/ui_lib.go +++ b/ethereal/ui_lib.go @@ -44,6 +44,10 @@ 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), filterCallbacks: make(map[int][]int), filters: make(map[int]*GuiFilter)} } +func (self *UiLib) Note(msg string) { + logger.Infoln(msg) +} + func (self *UiLib) LookupDomain(domain string) string { world := self.World() From 91ca5d724e1c65a7d9725b65ea0f003161301f63 Mon Sep 17 00:00:00 2001 From: obscuren Date: Sun, 14 Sep 2014 00:13:47 +0200 Subject: [PATCH 13/39] Reworked filters --- ethereal/assets/ext/filter.js | 40 +- ethereal/assets/qml/views/wallet.qml | 5 +- ethereal/assets/qml/wallet.qml | 1487 +++++++++++++------------- ethereal/gui.go | 36 + ethereal/ui_lib.go | 53 +- 5 files changed, 833 insertions(+), 788 deletions(-) diff --git a/ethereal/assets/ext/filter.js b/ethereal/assets/ext/filter.js index 6d6ec8748e22..bc6a93144eb1 100644 --- a/ethereal/assets/ext/filter.js +++ b/ethereal/assets/ext/filter.js @@ -1,31 +1,49 @@ +var ethx = { + prototype: Object, + + watch: function(options) { + return new Filter(options); + }, + + note: function() { + var args = Array.prototype.slice.call(arguments, 0); + var o = [] + for(var i = 0; i < args.length; i++) { + o.push(args[i].toString()) + } + + eth.notef(o); + }, +}; + var Filter = function(options) { - this.callbacks = {}; - this.seed = Math.floor(Math.random() * 1000000); + this.callbacks = []; this.options = options; if(options === "chain") { - eth.registerFilterString(options, this.seed); + this.id = eth.newFilterString(options); } else if(typeof options === "object") { - eth.registerFilter(options, this.seed); + this.id = eth.newFilter(options); } }; Filter.prototype.changed = function(callback) { - var cbseed = Math.floor(Math.random() * 1000000); - eth.registerFilterCallback(this.seed, cbseed); + this.callbacks.push(callback); var self = this; - message.connect(function(messages, seed, callbackSeed) { - if(seed == self.seed && callbackSeed == cbseed) { - callback.call(self, messages); + message.connect(function(messages, id) { + if(id == self.id) { + for(var i = 0; i < self.callbacks.length; i++) { + self.callbacks[i].call(self, messages); + } } }); }; Filter.prototype.uninstall = function() { - eth.uninstallFilter(this.seed) + eth.uninstallFilter(this.id) } Filter.prototype.messages = function() { - return JSON.parse(eth.messages(this.options)) + return eth.messages(this.id) } diff --git a/ethereal/assets/qml/views/wallet.qml b/ethereal/assets/qml/views/wallet.qml index c2513289ef52..2a766bb5b4e8 100644 --- a/ethereal/assets/qml/views/wallet.qml +++ b/ethereal/assets/qml/views/wallet.qml @@ -5,7 +5,6 @@ import QtQuick.Dialogs 1.0; import QtQuick.Window 2.1; import QtQuick.Controls.Styles 1.1 import Ethereum 1.0 -import "../../ext/filter.js" as Eth Rectangle { id: root @@ -152,7 +151,7 @@ Rectangle { model: ListModel { id: txModel Component.onCompleted: { - var filter = new Eth.Filter({latest: -1, from: eth.key().address}) + var filter = ethx.watch({latest: -1, from: eth.key().address}); filter.changed(addTxs) addTxs(filter.messages()) @@ -160,7 +159,7 @@ Rectangle { function addTxs(messages) { for(var i = 0; i < messages.length; i++) { - var message = messages[i]; + var message = messages.get(i); txModel.insert(0, {num: txModel.count, 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 0ddbd26bd235..bd4eeba1a24e 100644 --- a/ethereal/assets/qml/wallet.qml +++ b/ethereal/assets/qml/wallet.qml @@ -9,747 +9,748 @@ 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: "Ethegate" - - // 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(messages, receiverSeed, callbackSeed); - } - - 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 walletView = addPlugin("./views/wallet.qml", {noAdd: true, section: "ethereum", active: true}) - 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) { - try { - var component = Qt.createComponent(path); - if(component.status != Component.Ready) { - if(component.status == Component.Error) { - console.debug("Error:"+ component.errorString()); - } - - return - } - - var views = mainSplit.addComponent(component, options) - views.menuItem.path = path - - mainSplit.views.push(views); - - if(!options.noAdd) { - gui.addPlugin(path) - } - - return views.view - } catch(e) { - } - } - - MenuBar { - Menu { - title: "File" - MenuItem { - text: "Import App" - shortcut: "Ctrl+o" - onTriggered: { - generalFileDialog.show(true, importApp) - } - } - - MenuItem { - text: "Browser" - onTriggered: eth.openBrowser() - } - - MenuItem { - text: "Add plugin" - onTriggered: { - generalFileDialog.show(true, function(path) { - addPlugin(path, {canClose: true, section: "apps"}) - }) - } - } - - MenuSeparator {} - - MenuItem { - text: "Import key" - shortcut: "Ctrl+i" - onTriggered: { - generalFileDialog.show(true, function(path) { - gui.importKey(path) - }) - } - } - - MenuItem { - text: "Export keys" - shortcut: "Ctrl+e" - onTriggered: { - generalFileDialog.show(false, function(path) { - }) - } - } - } - - Menu { - title: "Developer" - MenuItem { - text: "Debugger" - shortcut: "Ctrl+d" - onTriggered: eth.startDebugger() - } - - MenuItem { - text: "Import Tx" - onTriggered: { - txImportDialog.visible = true - } - } - - MenuItem { - text: "Run JS file" - onTriggered: { - generalFileDialog.show(true, function(path) { - eth.evalJavascriptFile(path) - }) - } - } - - MenuItem { - text: "Dump state" - onTriggered: { - generalFileDialog.show(false, function(path) { - // Empty hash for latest - gui.dumpState("", path) - }) - } - } - - MenuSeparator {} - - MenuItem { - id: miningSpeed - text: "Mining: Turbo" - onTriggered: { - gui.toggleTurboMining() - if(text == "Mining: Turbo") { - text = "Mining: Normal"; - } else { - text = "Mining: Turbo"; - } - } - } - } - - Menu { - title: "Network" - MenuItem { - text: "Add Peer" - shortcut: "Ctrl+p" - onTriggered: { - addPeerWin.visible = true - } - } - MenuItem { - text: "Show Peers" - shortcut: "Ctrl+e" - onTriggered: { - peerWindow.visible = true - } - } - } - - Menu { - title: "Help" - MenuItem { - text: "About" - onTriggered: { - aboutWin.visible = true - } - } - } - - } - - statusBar: StatusBar { - height: 32 - RowLayout { - Button { - id: miningButton - text: "Start Mining" - onClicked: { - gui.toggleMining() - } - } - - Button { - id: importAppButton - text: "Browser" - onClicked: { - eth.openBrowser() - } - } - - RowLayout { - Label { - id: walletValueLabel - - font.pixelSize: 10 - styleColor: "#797979" - } - } - } - - Label { - y: 6 - objectName: "miningLabel" - visible: true - font.pixelSize: 10 - anchors.right: lastBlockLabel.left - anchors.rightMargin: 5 - } - - Label { - y: 6 - id: lastBlockLabel - objectName: "lastBlockLabel" - visible: true - text: "" - font.pixelSize: 10 - 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 - } - - Label { - id: peerLabel - font.pixelSize: 8 - text: "0 / 0" - } - Image { - id: peerImage - width: 10; height: 10 - source: "../network.png" - } - } - } - - - property var blockModel: ListModel { - id: blockModel - } - - SplitView { - property var views: []; - - id: mainSplit - anchors.fill: parent - resizing: false - - function setView(view, menu) { - for(var i = 0; i < views.length; i++) { - views[i].view.visible = false - - views[i].menuItem.border.color = "#00000000" - views[i].menuItem.color = "#00000000" - } - view.visible = true - - menu.border.color = "#CCCCCC" - menu.color = "#FFFFFFFF" - } - - function addComponent(component, options) { - var view = mainView.createView(component, options) - view.visible = false - view.anchors.fill = mainView - - if( !view.hasOwnProperty("iconSource") ) { - console.log("Could not load plugin. Property 'iconSourc' not found on view."); - return; - } - - var menuItem = menu.createMenuItem(view.iconSource, view, options); - if( view.hasOwnProperty("menuItem") ) { - view.menuItem = menuItem; - } - - if( view.hasOwnProperty("onReady") ) { - view.onReady.call(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" - - Component { - id: menuItemTemplate - Rectangle { - id: menuItem - property var view; - property var path; - - property alias title: label.text - property alias icon: icon.source - property alias secondaryTitle: secondary.text - - width: 180 - height: 28 - border.color: "#00000000" - border.width: 1 - radius: 5 - color: "#00000000" - - anchors { - left: parent.left - leftMargin: 4 - } - - MouseArea { - anchors.fill: parent - onClicked: { - mainSplit.setView(view, menuItem) - } - } - - Image { - id: icon - height: 20 - width: 20 - anchors { - left: parent.left - verticalCenter: parent.verticalCenter - leftMargin: 3 - } - MouseArea { - anchors.fill: parent - onClicked: { - menuItem.closeApp() - } - } - } - - Text { - id: label - anchors { - left: icon.right - verticalCenter: parent.verticalCenter - leftMargin: 3 - } - - color: "#0D0A01" - font.pixelSize: 12 - } - - 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.iconSource - /* - 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: "APPS" - font.bold: true - anchors { - left: parent.left - leftMargin: 5 - } - color: "#888888" - } - - ColumnLayout { - id: menuApps - spacing: 3 - anchors { - left: parent.left - right: parent.right - } - } - - 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; - } - } - - - } - - - /****************** - * Dialogs - *****************/ - FileDialog { - id: generalFileDialog - property var callback; - onAccepted: { - var path = this.fileUrl.toString(); - callback.call(this, path); - } - - function show(selectExisting, callback) { - generalFileDialog.callback = callback; - generalFileDialog.selectExisting = selectExisting; - - this.open(); - } - } - - - /****************** - * 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 setWalletValue(value) { - walletValueLabel.text = value - } - - function loadPlugin(name) { - console.log("Loading plugin" + name) - var view = 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 - Aitne


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 - } - } + id: root + + property alias miningButtonText: miningButton.text + property var ethx : Eth.ethx + + width: 900 + height: 600 + minimumHeight: 300 + + 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); + function invokeFilterCallback(data, receiverSeed) { + //var messages = JSON.parse(data) + // Signal handler + message(data, receiverSeed); + } + + TextField { + id: copyElementHax + visible: false + } + + function copyToClipboard(text) { + copyElementHax.text = text + copyElementHax.selectAll() + copyElementHax.copy() + } + + // Takes care of loading all default plugins + Component.onCompleted: { + addPlugin("./views/wallet.qml", {noAdd: true, section: "ethereum", active: true}); + + addPlugin("./views/transaction.qml", {noAdd: true, section: "legacy"}); + addPlugin("./views/chain.qml", {noAdd: true, section: "legacy"}); + addPlugin("./views/info.qml", {noAdd: true, section: "legacy"}); + addPlugin("./views/pending_tx.qml", {noAdd: true, section: "legacy"}); + addPlugin("./views/javascript.qml", {noAdd: true, section: "legacy"}); + + // Call the ready handler + gui.done(); + } + + function addPlugin(path, options) { + try { + var component = Qt.createComponent(path); + if(component.status != Component.Ready) { + if(component.status == Component.Error) { + ethx.note("error: ", component.errorString()); + } + + return + } + + var views = mainSplit.addComponent(component, options) + views.menuItem.path = path + + mainSplit.views.push(views); + + if(!options.noAdd) { + gui.addPlugin(path) + } + + return views.view + } catch(e) { + eth.note(e) + } + } + + MenuBar { + Menu { + title: "File" + MenuItem { + text: "Import App" + shortcut: "Ctrl+o" + onTriggered: { + generalFileDialog.show(true, importApp) + } + } + + MenuItem { + text: "Browser" + onTriggered: eth.openBrowser() + } + + MenuItem { + text: "Add plugin" + onTriggered: { + generalFileDialog.show(true, function(path) { + addPlugin(path, {canClose: true, section: "apps"}) + }) + } + } + + MenuSeparator {} + + MenuItem { + text: "Import key" + shortcut: "Ctrl+i" + onTriggered: { + generalFileDialog.show(true, function(path) { + gui.importKey(path) + }) + } + } + + MenuItem { + text: "Export keys" + shortcut: "Ctrl+e" + onTriggered: { + generalFileDialog.show(false, function(path) { + }) + } + } + } + + Menu { + title: "Developer" + MenuItem { + text: "Debugger" + shortcut: "Ctrl+d" + onTriggered: eth.startDebugger() + } + + MenuItem { + text: "Import Tx" + onTriggered: { + txImportDialog.visible = true + } + } + + MenuItem { + text: "Run JS file" + onTriggered: { + generalFileDialog.show(true, function(path) { + eth.evalJavascriptFile(path) + }) + } + } + + MenuItem { + text: "Dump state" + onTriggered: { + generalFileDialog.show(false, function(path) { + // Empty hash for latest + gui.dumpState("", path) + }) + } + } + + MenuSeparator {} + + MenuItem { + id: miningSpeed + text: "Mining: Turbo" + onTriggered: { + gui.toggleTurboMining() + if(text == "Mining: Turbo") { + text = "Mining: Normal"; + } else { + text = "Mining: Turbo"; + } + } + } + } + + Menu { + title: "Network" + MenuItem { + text: "Add Peer" + shortcut: "Ctrl+p" + onTriggered: { + addPeerWin.visible = true + } + } + MenuItem { + text: "Show Peers" + shortcut: "Ctrl+e" + onTriggered: { + peerWindow.visible = true + } + } + } + + Menu { + title: "Help" + MenuItem { + text: "About" + onTriggered: { + aboutWin.visible = true + } + } + } + + } + + statusBar: StatusBar { + height: 32 + RowLayout { + Button { + id: miningButton + text: "Start Mining" + onClicked: { + gui.toggleMining() + } + } + + Button { + id: importAppButton + text: "Browser" + onClicked: { + eth.openBrowser() + } + } + + RowLayout { + Label { + id: walletValueLabel + + font.pixelSize: 10 + styleColor: "#797979" + } + } + } + + Label { + y: 6 + objectName: "miningLabel" + visible: true + font.pixelSize: 10 + anchors.right: lastBlockLabel.left + anchors.rightMargin: 5 + } + + Label { + y: 6 + id: lastBlockLabel + objectName: "lastBlockLabel" + visible: true + text: "" + font.pixelSize: 10 + 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 + } + + Label { + id: peerLabel + font.pixelSize: 8 + text: "0 / 0" + } + Image { + id: peerImage + width: 10; height: 10 + source: "../network.png" + } + } + } + + + property var blockModel: ListModel { + id: blockModel + } + + SplitView { + property var views: []; + + id: mainSplit + anchors.fill: parent + resizing: false + + function setView(view, menu) { + for(var i = 0; i < views.length; i++) { + views[i].view.visible = false + + views[i].menuItem.border.color = "#00000000" + views[i].menuItem.color = "#00000000" + } + view.visible = true + + menu.border.color = "#CCCCCC" + menu.color = "#FFFFFFFF" + } + + function addComponent(component, options) { + var view = mainView.createView(component, options) + view.visible = false + view.anchors.fill = mainView + + if( !view.hasOwnProperty("iconSource") ) { + console.log("Could not load plugin. Property 'iconSourc' not found on view."); + return; + } + + var menuItem = menu.createMenuItem(view.iconSource, view, options); + if( view.hasOwnProperty("menuItem") ) { + view.menuItem = menuItem; + } + + if( view.hasOwnProperty("onReady") ) { + view.onReady.call(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" + + Component { + id: menuItemTemplate + Rectangle { + id: menuItem + property var view; + property var path; + + property alias title: label.text + property alias icon: icon.source + property alias secondaryTitle: secondary.text + + width: 180 + height: 28 + border.color: "#00000000" + border.width: 1 + radius: 5 + color: "#00000000" + + anchors { + left: parent.left + leftMargin: 4 + } + + MouseArea { + anchors.fill: parent + onClicked: { + mainSplit.setView(view, menuItem) + } + } + + Image { + id: icon + height: 20 + width: 20 + anchors { + left: parent.left + verticalCenter: parent.verticalCenter + leftMargin: 3 + } + MouseArea { + anchors.fill: parent + onClicked: { + menuItem.closeApp() + } + } + } + + Text { + id: label + anchors { + left: icon.right + verticalCenter: parent.verticalCenter + leftMargin: 3 + } + + color: "#0D0A01" + font.pixelSize: 12 + } + + 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.iconSource + /* + 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: "APPS" + font.bold: true + anchors { + left: parent.left + leftMargin: 5 + } + color: "#888888" + } + + ColumnLayout { + id: menuApps + spacing: 3 + anchors { + left: parent.left + right: parent.right + } + } + + 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; + } + } + + + } + + + /****************** + * Dialogs + *****************/ + FileDialog { + id: generalFileDialog + property var callback; + onAccepted: { + var path = this.fileUrl.toString(); + callback.call(this, path); + } + + function show(selectExisting, callback) { + generalFileDialog.callback = callback; + generalFileDialog.selectExisting = selectExisting; + + this.open(); + } + } + + + /****************** + * 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 setWalletValue(value) { + walletValueLabel.text = value + } + + function loadPlugin(name) { + console.log("Loading plugin" + name) + var view = 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 - Aitne


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 33fd35e6e559..7bb4612c2b26 100644 --- a/ethereal/gui.go +++ b/ethereal/gui.go @@ -1,5 +1,8 @@ package main +// #include "/Users/jeffrey/go/src/github.com/go-qml/qml/cpp/capi.h" +import "C" + import ( "bytes" "encoding/json" @@ -10,7 +13,9 @@ import ( "strconv" "strings" "time" + "unsafe" + "bitbucket.org/binet/go-ffi/pkg/ffi" "github.com/ethereum/eth-go" "github.com/ethereum/eth-go/ethchain" "github.com/ethereum/eth-go/ethdb" @@ -23,6 +28,29 @@ import ( "gopkg.in/qml.v1" ) +func LoadExtension(path string) (uintptr, error) { + lib, err := ffi.NewLibrary(path) + if err != nil { + return 0, err + } + + so, err := lib.Fct("sharedObject", ffi.Pointer, nil) + if err != nil { + return 0, err + } + + ptr := so() + + /* + err = lib.Close() + if err != nil { + return 0, err + } + */ + + return ptr.Interface().(uintptr), nil +} + var logger = ethlog.NewLogger("GUI") type Gui struct { @@ -91,6 +119,14 @@ func (gui *Gui) Start(assetPath string) { context.SetVar("gui", gui) context.SetVar("eth", gui.uiLib) + vec, errr := LoadExtension("/Users/jeffrey/Desktop/build-libqmltest-Desktop_Qt_5_2_1_clang_64bit-Debug/liblibqmltest_debug.dylib") + fmt.Printf("Fetched vec with addr: %#x\n", vec) + if errr != nil { + fmt.Println(errr) + } else { + context.SetVar("vec", (unsafe.Pointer)(vec)) + } + // Load the main QML interface data, _ := ethutil.Config.Db.Get([]byte("KeyRing")) diff --git a/ethereal/ui_lib.go b/ethereal/ui_lib.go index 80e711aa7bf7..4e1f7f45c4cd 100644 --- a/ethereal/ui_lib.go +++ b/ethereal/ui_lib.go @@ -37,15 +37,15 @@ type UiLib struct { jsEngine *javascript.JSRE filterCallbacks map[int][]int - filters map[int]*GuiFilter + //filters map[int]*ethpipe.JSFilter } 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), filterCallbacks: make(map[int][]int), filters: make(map[int]*GuiFilter)} + 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]*ethpipe.JSFilter)} } -func (self *UiLib) Note(msg string) { - logger.Infoln(msg) +func (self *UiLib) Notef(args []interface{}) { + logger.Infoln(args...) } func (self *UiLib) LookupDomain(domain string) string { @@ -164,46 +164,37 @@ 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 - +func (self *UiLib) NewFilter(object map[string]interface{}) int { + filter, id := self.eth.InstallFilter(object) filter.MessageCallback = func(messages ethstate.Messages) { - for _, callbackSeed := range self.filterCallbacks[seed] { - self.win.Root().Call("invokeFilterCallback", filter.MessagesToJson(messages), seed, callbackSeed) - } + self.win.Root().Call("invokeFilterCallback", ethpipe.ToJSMessages(messages), id) } + return id } -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) NewFilterString(typ string) int { + filter, id := self.eth.InstallFilter(nil) + filter.BlockCallback = func(block *ethchain.Block) { + self.win.Root().Call("invokeFilterCallback", "{}", id) } -} -func (self *UiLib) RegisterFilterCallback(seed, cbSeed int) { - self.filterCallbacks[seed] = append(self.filterCallbacks[seed], cbSeed) + return id } -func (self *UiLib) UninstallFilter(seed int) { - filter := self.filters[seed] +func (self *UiLib) Messages(id int) *ethutil.List { + filter := self.eth.GetFilter(id) if filter != nil { - filter.Uninstall() - delete(self.filters, seed) + messages := filter.Find() + + return ethpipe.ToJSMessages(messages) } + + return ethutil.EmptyList() } -type GuiFilter struct { - *ethpipe.JSFilter - seed int +func (self *UiLib) UninstallFilter(id int) { + self.eth.UninstallFilter(id) } func (self *UiLib) Transact(object map[string]interface{}) (*ethpipe.JSReceipt, error) { From ddefa11695f9fabaaa0a5f76fe754571829df6ec Mon Sep 17 00:00:00 2001 From: obscuren Date: Sun, 14 Sep 2014 12:02:08 +0200 Subject: [PATCH 14/39] Minor updates to the new filter --- ethereal/assets/qml/views/transaction.qml | 2 ++ ethereal/assets/qml/wallet.qml | 2 +- ethereal/gui.go | 17 +++++++++-------- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/ethereal/assets/qml/views/transaction.qml b/ethereal/assets/qml/views/transaction.qml index fb8ba8a6d75e..7d689733fa32 100644 --- a/ethereal/assets/qml/views/transaction.qml +++ b/ethereal/assets/qml/views/transaction.qml @@ -180,6 +180,8 @@ Rectangle { txResult.text = "Your transaction has been submitted:\n" txOutput.text = res[0].address mainContractColumn.state = "DONE" + + console.log(res) } } } diff --git a/ethereal/assets/qml/wallet.qml b/ethereal/assets/qml/wallet.qml index bd4eeba1a24e..4d8561491411 100644 --- a/ethereal/assets/qml/wallet.qml +++ b/ethereal/assets/qml/wallet.qml @@ -76,7 +76,7 @@ ApplicationWindow { return views.view } catch(e) { - eth.note(e) + ethx.note(e) } } diff --git a/ethereal/gui.go b/ethereal/gui.go index 7bb4612c2b26..173974c4bbba 100644 --- a/ethereal/gui.go +++ b/ethereal/gui.go @@ -13,7 +13,6 @@ import ( "strconv" "strings" "time" - "unsafe" "bitbucket.org/binet/go-ffi/pkg/ffi" "github.com/ethereum/eth-go" @@ -119,13 +118,15 @@ func (gui *Gui) Start(assetPath string) { context.SetVar("gui", gui) context.SetVar("eth", gui.uiLib) - vec, errr := LoadExtension("/Users/jeffrey/Desktop/build-libqmltest-Desktop_Qt_5_2_1_clang_64bit-Debug/liblibqmltest_debug.dylib") - fmt.Printf("Fetched vec with addr: %#x\n", vec) - if errr != nil { - fmt.Println(errr) - } else { - context.SetVar("vec", (unsafe.Pointer)(vec)) - } + /* + vec, errr := LoadExtension("/Users/jeffrey/Desktop/build-libqmltest-Desktop_Qt_5_2_1_clang_64bit-Debug/liblibqmltest_debug.dylib") + fmt.Printf("Fetched vec with addr: %#x\n", vec) + if errr != nil { + fmt.Println(errr) + } else { + context.SetVar("vec", (unsafe.Pointer)(vec)) + } + */ // Load the main QML interface data, _ := ethutil.Config.Db.Get([]byte("KeyRing")) From 3dfda15ef39c4d08ba27ffd9f4a8350aa817e6e0 Mon Sep 17 00:00:00 2001 From: obscuren Date: Sun, 14 Sep 2014 13:27:20 +0200 Subject: [PATCH 15/39] removed ffi --- ethereal/flags.go | 7 ++++--- ethereal/gui.go | 6 ++---- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/ethereal/flags.go b/ethereal/flags.go index c9327c3d31b4..382f093aa4d6 100644 --- a/ethereal/flags.go +++ b/ethereal/flags.go @@ -1,15 +1,16 @@ package main import ( - "bitbucket.org/kardianos/osext" "flag" "fmt" - "github.com/ethereum/eth-go/ethlog" "os" "os/user" "path" "path/filepath" "runtime" + + "bitbucket.org/kardianos/osext" + "github.com/ethereum/eth-go/ethlog" ) var Identifier string @@ -78,7 +79,7 @@ func Init() { flag.StringVar(&KeyRing, "keyring", "", "identifier for keyring to use") flag.StringVar(&KeyStore, "keystore", "db", "system to store keyrings: db|file (db)") flag.StringVar(&OutboundPort, "port", "30303", "listening port") - flag.BoolVar(&UseUPnP, "upnp", false, "enable UPnP support") + flag.BoolVar(&UseUPnP, "upnp", true, "enable UPnP support") flag.IntVar(&MaxPeer, "maxpeer", 10, "maximum desired peers") flag.IntVar(&RpcPort, "rpcport", 8080, "port to start json-rpc server on") flag.BoolVar(&StartRpc, "rpc", false, "start rpc server") diff --git a/ethereal/gui.go b/ethereal/gui.go index 173974c4bbba..0b1b322ad2aa 100644 --- a/ethereal/gui.go +++ b/ethereal/gui.go @@ -13,8 +13,6 @@ import ( "strconv" "strings" "time" - - "bitbucket.org/binet/go-ffi/pkg/ffi" "github.com/ethereum/eth-go" "github.com/ethereum/eth-go/ethchain" "github.com/ethereum/eth-go/ethdb" @@ -27,6 +25,7 @@ import ( "gopkg.in/qml.v1" ) +/* func LoadExtension(path string) (uintptr, error) { lib, err := ffi.NewLibrary(path) if err != nil { @@ -40,15 +39,14 @@ func LoadExtension(path string) (uintptr, error) { ptr := so() - /* err = lib.Close() if err != nil { return 0, err } - */ return ptr.Interface().(uintptr), nil } +*/ var logger = ethlog.NewLogger("GUI") From 18bf586d553f46534f327281658e4b92bbbbab9a Mon Sep 17 00:00:00 2001 From: obscuren Date: Sun, 14 Sep 2014 13:28:28 +0200 Subject: [PATCH 16/39] Removed --- ethereal/gui.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ethereal/gui.go b/ethereal/gui.go index 0b1b322ad2aa..208157d69c71 100644 --- a/ethereal/gui.go +++ b/ethereal/gui.go @@ -1,6 +1,5 @@ package main -// #include "/Users/jeffrey/go/src/github.com/go-qml/qml/cpp/capi.h" import "C" import ( @@ -13,6 +12,7 @@ import ( "strconv" "strings" "time" + "github.com/ethereum/eth-go" "github.com/ethereum/eth-go/ethchain" "github.com/ethereum/eth-go/ethdb" From d22db772480c517a853546d9ccbfb51367982303 Mon Sep 17 00:00:00 2001 From: obscuren Date: Mon, 15 Sep 2014 22:11:36 +0200 Subject: [PATCH 17/39] Upped version --- ethereal/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ethereal/main.go b/ethereal/main.go index ac94a66229a5..dff0abbb7a82 100644 --- a/ethereal/main.go +++ b/ethereal/main.go @@ -12,7 +12,7 @@ import ( const ( ClientIdentifier = "Ethereal" - Version = "0.6.5" + Version = "0.6.6" ) var ethereum *eth.Ethereum From b89d9f6e90a561725899dd9bffb670efbf766386 Mon Sep 17 00:00:00 2001 From: obscuren Date: Tue, 16 Sep 2014 11:36:04 +0200 Subject: [PATCH 18/39] Added DApp url bar (TBD) & changed behaviour for the menu selection --- ethereal/assets/ext/http.js | 13 + ethereal/assets/qml/wallet.qml | 1602 +++++++++++++++++--------------- 2 files changed, 871 insertions(+), 744 deletions(-) create mode 100644 ethereal/assets/ext/http.js diff --git a/ethereal/assets/ext/http.js b/ethereal/assets/ext/http.js new file mode 100644 index 000000000000..725ce8e6b02d --- /dev/null +++ b/ethereal/assets/ext/http.js @@ -0,0 +1,13 @@ +// this function is included locally, but you can also include separately via a header definition +function request(url, callback) { + var xhr = new XMLHttpRequest(); + xhr.onreadystatechange = (function(req) { + return function() { + if(req.readyState === 4) { + callback(req); + } + } + })(xhr); + xhr.open('GET', url, true); + xhr.send(''); +} diff --git a/ethereal/assets/qml/wallet.qml b/ethereal/assets/qml/wallet.qml index 4d8561491411..0b495397220a 100644 --- a/ethereal/assets/qml/wallet.qml +++ b/ethereal/assets/qml/wallet.qml @@ -7,750 +7,864 @@ import QtQuick.Controls.Styles 1.1 import Ethereum 1.0 import "../ext/filter.js" as Eth +import "../ext/http.js" as Http ApplicationWindow { - id: root - - property alias miningButtonText: miningButton.text - property var ethx : Eth.ethx - - width: 900 - height: 600 - minimumHeight: 300 - - 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); - function invokeFilterCallback(data, receiverSeed) { - //var messages = JSON.parse(data) - // Signal handler - message(data, receiverSeed); - } - - TextField { - id: copyElementHax - visible: false - } - - function copyToClipboard(text) { - copyElementHax.text = text - copyElementHax.selectAll() - copyElementHax.copy() - } - - // Takes care of loading all default plugins - Component.onCompleted: { - addPlugin("./views/wallet.qml", {noAdd: true, section: "ethereum", active: true}); - - addPlugin("./views/transaction.qml", {noAdd: true, section: "legacy"}); - addPlugin("./views/chain.qml", {noAdd: true, section: "legacy"}); - addPlugin("./views/info.qml", {noAdd: true, section: "legacy"}); - addPlugin("./views/pending_tx.qml", {noAdd: true, section: "legacy"}); - addPlugin("./views/javascript.qml", {noAdd: true, section: "legacy"}); - - // Call the ready handler - gui.done(); - } - - function addPlugin(path, options) { - try { - var component = Qt.createComponent(path); - if(component.status != Component.Ready) { - if(component.status == Component.Error) { - ethx.note("error: ", component.errorString()); - } - - return - } - - var views = mainSplit.addComponent(component, options) - views.menuItem.path = path - - mainSplit.views.push(views); - - if(!options.noAdd) { - gui.addPlugin(path) - } - - return views.view - } catch(e) { - ethx.note(e) - } - } - - MenuBar { - Menu { - title: "File" - MenuItem { - text: "Import App" - shortcut: "Ctrl+o" - onTriggered: { - generalFileDialog.show(true, importApp) - } - } - - MenuItem { - text: "Browser" - onTriggered: eth.openBrowser() - } - - MenuItem { - text: "Add plugin" - onTriggered: { - generalFileDialog.show(true, function(path) { - addPlugin(path, {canClose: true, section: "apps"}) - }) - } - } - - MenuSeparator {} - - MenuItem { - text: "Import key" - shortcut: "Ctrl+i" - onTriggered: { - generalFileDialog.show(true, function(path) { - gui.importKey(path) - }) - } - } - - MenuItem { - text: "Export keys" - shortcut: "Ctrl+e" - onTriggered: { - generalFileDialog.show(false, function(path) { - }) - } - } - } - - Menu { - title: "Developer" - MenuItem { - text: "Debugger" - shortcut: "Ctrl+d" - onTriggered: eth.startDebugger() - } - - MenuItem { - text: "Import Tx" - onTriggered: { - txImportDialog.visible = true - } - } - - MenuItem { - text: "Run JS file" - onTriggered: { - generalFileDialog.show(true, function(path) { - eth.evalJavascriptFile(path) - }) - } - } - - MenuItem { - text: "Dump state" - onTriggered: { - generalFileDialog.show(false, function(path) { - // Empty hash for latest - gui.dumpState("", path) - }) - } - } - - MenuSeparator {} - - MenuItem { - id: miningSpeed - text: "Mining: Turbo" - onTriggered: { - gui.toggleTurboMining() - if(text == "Mining: Turbo") { - text = "Mining: Normal"; - } else { - text = "Mining: Turbo"; - } - } - } - } - - Menu { - title: "Network" - MenuItem { - text: "Add Peer" - shortcut: "Ctrl+p" - onTriggered: { - addPeerWin.visible = true - } - } - MenuItem { - text: "Show Peers" - shortcut: "Ctrl+e" - onTriggered: { - peerWindow.visible = true - } - } - } - - Menu { - title: "Help" - MenuItem { - text: "About" - onTriggered: { - aboutWin.visible = true - } - } - } - - } - - statusBar: StatusBar { - height: 32 - RowLayout { - Button { - id: miningButton - text: "Start Mining" - onClicked: { - gui.toggleMining() - } - } - - Button { - id: importAppButton - text: "Browser" - onClicked: { - eth.openBrowser() - } - } - - RowLayout { - Label { - id: walletValueLabel - - font.pixelSize: 10 - styleColor: "#797979" - } - } - } - - Label { - y: 6 - objectName: "miningLabel" - visible: true - font.pixelSize: 10 - anchors.right: lastBlockLabel.left - anchors.rightMargin: 5 - } - - Label { - y: 6 - id: lastBlockLabel - objectName: "lastBlockLabel" - visible: true - text: "" - font.pixelSize: 10 - 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 - } - - Label { - id: peerLabel - font.pixelSize: 8 - text: "0 / 0" - } - Image { - id: peerImage - width: 10; height: 10 - source: "../network.png" - } - } - } - - - property var blockModel: ListModel { - id: blockModel - } - - SplitView { - property var views: []; - - id: mainSplit - anchors.fill: parent - resizing: false - - function setView(view, menu) { - for(var i = 0; i < views.length; i++) { - views[i].view.visible = false - - views[i].menuItem.border.color = "#00000000" - views[i].menuItem.color = "#00000000" - } - view.visible = true - - menu.border.color = "#CCCCCC" - menu.color = "#FFFFFFFF" - } - - function addComponent(component, options) { - var view = mainView.createView(component, options) - view.visible = false - view.anchors.fill = mainView - - if( !view.hasOwnProperty("iconSource") ) { - console.log("Could not load plugin. Property 'iconSourc' not found on view."); - return; - } - - var menuItem = menu.createMenuItem(view.iconSource, view, options); - if( view.hasOwnProperty("menuItem") ) { - view.menuItem = menuItem; - } - - if( view.hasOwnProperty("onReady") ) { - view.onReady.call(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" - - Component { - id: menuItemTemplate - Rectangle { - id: menuItem - property var view; - property var path; - - property alias title: label.text - property alias icon: icon.source - property alias secondaryTitle: secondary.text - - width: 180 - height: 28 - border.color: "#00000000" - border.width: 1 - radius: 5 - color: "#00000000" - - anchors { - left: parent.left - leftMargin: 4 - } - - MouseArea { - anchors.fill: parent - onClicked: { - mainSplit.setView(view, menuItem) - } - } - - Image { - id: icon - height: 20 - width: 20 - anchors { - left: parent.left - verticalCenter: parent.verticalCenter - leftMargin: 3 - } - MouseArea { - anchors.fill: parent - onClicked: { - menuItem.closeApp() - } - } - } - - Text { - id: label - anchors { - left: icon.right - verticalCenter: parent.verticalCenter - leftMargin: 3 - } - - color: "#0D0A01" - font.pixelSize: 12 - } - - 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.iconSource - /* - 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: "APPS" - font.bold: true - anchors { - left: parent.left - leftMargin: 5 - } - color: "#888888" - } - - ColumnLayout { - id: menuApps - spacing: 3 - anchors { - left: parent.left - right: parent.right - } - } - - 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; - } - } - - - } - - - /****************** - * Dialogs - *****************/ - FileDialog { - id: generalFileDialog - property var callback; - onAccepted: { - var path = this.fileUrl.toString(); - callback.call(this, path); - } - - function show(selectExisting, callback) { - generalFileDialog.callback = callback; - generalFileDialog.selectExisting = selectExisting; - - this.open(); - } - } - - - /****************** - * 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 setWalletValue(value) { - walletValueLabel.text = value - } - - function loadPlugin(name) { - console.log("Loading plugin" + name) - var view = 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 - Aitne


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 - } - } + id: root + + property alias miningButtonText: miningButton.text + property var ethx : Eth.ethx + + width: 900 + height: 600 + minimumHeight: 300 + + 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); + function invokeFilterCallback(data, receiverSeed) { + //var messages = JSON.parse(data) + // Signal handler + message(data, receiverSeed); + } + + TextField { + id: copyElementHax + visible: false + } + + function copyToClipboard(text) { + copyElementHax.text = text + copyElementHax.selectAll() + copyElementHax.copy() + } + + // Takes care of loading all default plugins + Component.onCompleted: { + addPlugin("./views/wallet.qml", {noAdd: true, section: "ethereum", active: true}); + + addPlugin("./views/transaction.qml", {noAdd: true, section: "legacy"}); + addPlugin("./views/chain.qml", {noAdd: true, section: "legacy"}); + addPlugin("./views/info.qml", {noAdd: true, section: "legacy"}); + addPlugin("./views/pending_tx.qml", {noAdd: true, section: "legacy"}); + addPlugin("./views/javascript.qml", {noAdd: true, section: "legacy"}); + + // Call the ready handler + gui.done(); + } + + function addViews(view, path, options) { + var views = mainSplit.addComponent(view, options) + views.menuItem.path = path + + mainSplit.views.push(views); + + if(!options.noAdd) { + gui.addPlugin(path) + } + + return views + } + + function addPlugin(path, options) { + try { + if(typeof(path) === "string" && /^https?/.test(path)) { + console.log('load http') + Http.request(path, function(o) { + if(o.status === 200) { + var view = Qt.createQmlObject(o.responseText, mainView, path) + addViews(view, path, options) + } + }) + + return + } + + var component = Qt.createComponent(path); + if(component.status != Component.Ready) { + if(component.status == Component.Error) { + ethx.note("error: ", component.errorString()); + } + + return + } + + var view = mainView.createView(component, options) + var views = addViews(view, path, options) + + return views.view + } catch(e) { + ethx.note(e) + } + } + + MenuBar { + Menu { + title: "File" + MenuItem { + text: "Import App" + shortcut: "Ctrl+o" + onTriggered: { + generalFileDialog.show(true, importApp) + } + } + + MenuItem { + text: "Browser" + onTriggered: eth.openBrowser() + } + + MenuItem { + text: "Add plugin" + onTriggered: { + generalFileDialog.show(true, function(path) { + addPlugin(path, {canClose: true, section: "apps"}) + }) + } + } + + MenuSeparator {} + + MenuItem { + text: "Import key" + shortcut: "Ctrl+i" + onTriggered: { + generalFileDialog.show(true, function(path) { + gui.importKey(path) + }) + } + } + + MenuItem { + text: "Export keys" + shortcut: "Ctrl+e" + onTriggered: { + generalFileDialog.show(false, function(path) { + }) + } + } + } + + Menu { + title: "Developer" + MenuItem { + text: "Debugger" + shortcut: "Ctrl+d" + onTriggered: eth.startDebugger() + } + + MenuItem { + text: "Import Tx" + onTriggered: { + txImportDialog.visible = true + } + } + + MenuItem { + text: "Run JS file" + onTriggered: { + generalFileDialog.show(true, function(path) { + eth.evalJavascriptFile(path) + }) + } + } + + MenuItem { + text: "Dump state" + onTriggered: { + generalFileDialog.show(false, function(path) { + // Empty hash for latest + gui.dumpState("", path) + }) + } + } + + MenuSeparator {} + + MenuItem { + id: miningSpeed + text: "Mining: Turbo" + onTriggered: { + gui.toggleTurboMining() + if(text == "Mining: Turbo") { + text = "Mining: Normal"; + } else { + text = "Mining: Turbo"; + } + } + } + } + + Menu { + title: "Network" + MenuItem { + text: "Add Peer" + shortcut: "Ctrl+p" + onTriggered: { + addPeerWin.visible = true + } + } + MenuItem { + text: "Show Peers" + shortcut: "Ctrl+e" + onTriggered: { + peerWindow.visible = true + } + } + } + + Menu { + title: "Help" + MenuItem { + text: "About" + onTriggered: { + aboutWin.visible = true + } + } + } + + } + + statusBar: StatusBar { + height: 32 + RowLayout { + Button { + id: miningButton + text: "Start Mining" + onClicked: { + gui.toggleMining() + } + } + + Button { + id: importAppButton + text: "Browser" + onClicked: { + eth.openBrowser() + } + } + + RowLayout { + Label { + id: walletValueLabel + + font.pixelSize: 10 + styleColor: "#797979" + } + } + } + + Label { + y: 6 + objectName: "miningLabel" + visible: true + font.pixelSize: 10 + anchors.right: lastBlockLabel.left + anchors.rightMargin: 5 + } + + Label { + y: 6 + id: lastBlockLabel + objectName: "lastBlockLabel" + visible: true + text: "" + font.pixelSize: 10 + 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 + } + + Label { + id: peerLabel + font.pixelSize: 8 + text: "0 / 0" + } + Image { + id: peerImage + width: 10; height: 10 + source: "../network.png" + } + } + } + + + property var blockModel: ListModel { + id: blockModel + } + + SplitView { + property var views: []; + + id: mainSplit + anchors.fill: parent + resizing: false + + function setView(view, menu) { + for(var i = 0; i < views.length; i++) { + views[i].view.visible = false + views[i].menuItem.setSelection(false) + } + view.visible = true + + //menu.border.color = "#CCCCCC" + //menu.color = "#FFFFFFFF" + menu.setSelection(true) + } + + function addComponent(view, options) { + view.visible = false + view.anchors.fill = mainView + + if( !view.hasOwnProperty("iconSource") ) { + console.log("Could not load plugin. Property 'iconSourc' not found on view."); + return; + } + + var menuItem = menu.createMenuItem(view.iconSource, view, options); + if( view.hasOwnProperty("menuItem") ) { + view.menuItem = menuItem; + } + + if( view.hasOwnProperty("onReady") ) { + view.onReady.call(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" + + Component { + id: menuItemTemplate + Rectangle { + id: menuItem + property var view; + property var path; + + property alias title: label.text + property alias icon: icon.source + property alias secondaryTitle: secondary.text + function setSelection(on) { + sel.visible = on + } + + width: 176 + height: 28 + color: "#00000000" + + anchors { + left: parent.left + leftMargin: 4 + } + + Rectangle { + id: sel + visible: false + anchors.fill: parent + color: "#00000000" + Rectangle { + id: r + anchors.fill: parent + border.color: "#CCCCCC" + border.width: 1 + radius: 5 + color: "#FFFFFFFF" + } + Rectangle { + anchors { + top: r.top + bottom: r.bottom + right: r.right + } + width: 10 + color: "#FFFFFFFF" + + Rectangle { + anchors { + left: parent.left + right: parent.right + top: parent.top + } + height: 1 + color: "#CCCCCC" + } + + Rectangle { + anchors { + left: parent.left + right: parent.right + bottom: parent.bottom + } + height: 1 + color: "#CCCCCC" + } + } + } + + MouseArea { + anchors.fill: parent + onClicked: { + mainSplit.setView(view, menuItem) + } + } + + Image { + id: icon + height: 20 + width: 20 + anchors { + left: parent.left + verticalCenter: parent.verticalCenter + leftMargin: 3 + } + MouseArea { + anchors.fill: parent + onClicked: { + menuItem.closeApp() + } + } + } + + Text { + id: label + anchors { + left: icon.right + verticalCenter: parent.verticalCenter + leftMargin: 3 + } + + color: "#0D0A01" + font.pixelSize: 12 + } + + 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.iconSource + /* + 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: "APPS" + font.bold: true + anchors { + left: parent.left + leftMargin: 5 + } + color: "#888888" + } + + ColumnLayout { + id: menuApps + spacing: 3 + anchors { + left: parent.left + right: parent.right + } + } + + 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 { + anchors.right: parent.right + anchors.left: menu.right + anchors.bottom: parent.bottom + anchors.top: parent.top + color: "#00000000" + + Rectangle { + id: urlPane + height: 40 + color: "#00000000" + anchors { + left: parent.left + right: parent.right + leftMargin: 5 + rightMargin: 5 + top: parent.top + topMargin: 5 + } + TextField { + id: url + objectName: "url" + placeholderText: "DApp URL" + anchors { + left: parent.left + right: parent.right + top: parent.top + topMargin: 5 + rightMargin: 5 + leftMargin: 5 + } + + Keys.onReturnPressed: { + addPlugin(this.text, {canClose: true, section: "apps"}) + } + } + + } + + // Border + Rectangle { + id: divider + anchors { + left: parent.left + right: parent.right + top: urlPane.bottom + } + z: -1 + height: 1 + color: "#CCCCCC" + } + + Rectangle { + id: mainView + + + anchors.right: parent.right + anchors.left: parent.left + anchors.bottom: parent.bottom + anchors.top: divider.bottom + + function createView(component) { + var view = component.createObject(mainView) + + return view; + } + } + } + } + + + /****************** + * Dialogs + *****************/ + FileDialog { + id: generalFileDialog + property var callback; + onAccepted: { + var path = this.fileUrl.toString(); + callback.call(this, path); + } + + function show(selectExisting, callback) { + generalFileDialog.callback = callback; + generalFileDialog.selectExisting = selectExisting; + + this.open(); + } + } + + + /****************** + * 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 setWalletValue(value) { + walletValueLabel.text = value + } + + function loadPlugin(name) { + console.log("Loading plugin" + name) + var view = 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 - Aitne


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" + text: "54.76.56.74:30303" + 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 + } + } } From 15ded0bea9600f489d7f9fb5430c26a84a021bd2 Mon Sep 17 00:00:00 2001 From: obscuren Date: Tue, 16 Sep 2014 16:36:46 +0200 Subject: [PATCH 19/39] Integrate web app in to the main client --- ethereal/assets/browser.png | Bin 0 -> 12903 bytes ethereal/assets/qml/wallet.qml | 17 +++++++++++++++++ ethereal/assets/qml/webapp.qml | 19 ++++++++++++++----- 3 files changed, 31 insertions(+), 5 deletions(-) create mode 100644 ethereal/assets/browser.png diff --git a/ethereal/assets/browser.png b/ethereal/assets/browser.png new file mode 100644 index 0000000000000000000000000000000000000000..1d7348170f7cadb76a1314a1d4fdcb03ab1abdd9 GIT binary patch literal 12903 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 Date: Wed, 17 Sep 2014 15:58:26 +0200 Subject: [PATCH 20/39] Renamed ethereal --- {ethereal => Mist}/assets/back.png | Bin {ethereal => Mist}/assets/browser.png | Bin {ethereal => Mist}/assets/bug.png | Bin {ethereal => Mist}/assets/close.png | Bin {ethereal => Mist}/assets/debugger/debugger.qml | 0 {ethereal => Mist}/assets/ext/big.js | 0 {ethereal => Mist}/assets/ext/ethereum.js | 0 {ethereal => Mist}/assets/ext/filter.js | 0 {ethereal => Mist}/assets/ext/home.html | 0 {ethereal => Mist}/assets/ext/http.js | 0 {ethereal => Mist}/assets/ext/pre.js | 0 {ethereal => Mist}/assets/ext/string.js | 0 {ethereal => Mist}/assets/ext/test.html | 0 {ethereal => Mist}/assets/facet.png | Bin {ethereal => Mist}/assets/heart.png | Bin {ethereal => Mist}/assets/muted/codemirror.css | 0 {ethereal => Mist}/assets/muted/debugger.html | 0 {ethereal => Mist}/assets/muted/eclipse.css | 0 {ethereal => Mist}/assets/muted/index.html | 0 {ethereal => Mist}/assets/muted/lib/codemirror.js | 0 {ethereal => Mist}/assets/muted/lib/go.js | 0 .../assets/muted/lib/matchbrackets.js | 0 {ethereal => Mist}/assets/muted/muted.js | 0 {ethereal => Mist}/assets/net.png | Bin {ethereal => Mist}/assets/network.png | Bin {ethereal => Mist}/assets/new.png | Bin {ethereal => Mist}/assets/pick.png | Bin {ethereal => Mist}/assets/qml/QmlApp.qml | 0 {ethereal => Mist}/assets/qml/first_run.qml | 0 {ethereal => Mist}/assets/qml/muted.qml | 0 {ethereal => Mist}/assets/qml/test_app.qml | 0 {ethereal => Mist}/assets/qml/transactions.qml | 0 {ethereal => Mist}/assets/qml/views/chain.qml | 0 {ethereal => Mist}/assets/qml/views/history.qml | 0 {ethereal => Mist}/assets/qml/views/info.qml | 0 {ethereal => Mist}/assets/qml/views/javascript.qml | 0 {ethereal => Mist}/assets/qml/views/pending_tx.qml | 0 {ethereal => Mist}/assets/qml/views/transaction.qml | 0 {ethereal => Mist}/assets/qml/views/wallet.qml | 0 {ethereal => Mist}/assets/qml/wallet.qml | 0 {ethereal => Mist}/assets/qml/webapp.qml | 0 {ethereal => Mist}/assets/tx.png | Bin {ethereal => Mist}/assets/util/test.html | 0 {ethereal => Mist}/assets/wallet.png | Bin {ethereal => Mist}/bindings.go | 0 {ethereal => Mist}/debugger.go | 0 {ethereal => Mist}/errors.go | 0 {ethereal => Mist}/ext_app.go | 0 {ethereal => Mist}/flags.go | 0 {ethereal => Mist}/gui.go | 0 {ethereal => Mist}/html_container.go | 0 {ethereal => Mist}/main.go | 0 {ethereal => Mist}/qml_container.go | 0 {ethereal => Mist}/ui_lib.go | 0 54 files changed, 0 insertions(+), 0 deletions(-) rename {ethereal => Mist}/assets/back.png (100%) rename {ethereal => Mist}/assets/browser.png (100%) rename {ethereal => Mist}/assets/bug.png (100%) rename {ethereal => Mist}/assets/close.png (100%) rename {ethereal => Mist}/assets/debugger/debugger.qml (100%) rename {ethereal => Mist}/assets/ext/big.js (100%) rename {ethereal => Mist}/assets/ext/ethereum.js (100%) rename {ethereal => Mist}/assets/ext/filter.js (100%) rename {ethereal => Mist}/assets/ext/home.html (100%) rename {ethereal => Mist}/assets/ext/http.js (100%) rename {ethereal => Mist}/assets/ext/pre.js (100%) rename {ethereal => Mist}/assets/ext/string.js (100%) rename {ethereal => Mist}/assets/ext/test.html (100%) rename {ethereal => Mist}/assets/facet.png (100%) rename {ethereal => Mist}/assets/heart.png (100%) rename {ethereal => Mist}/assets/muted/codemirror.css (100%) rename {ethereal => Mist}/assets/muted/debugger.html (100%) rename {ethereal => Mist}/assets/muted/eclipse.css (100%) rename {ethereal => Mist}/assets/muted/index.html (100%) rename {ethereal => Mist}/assets/muted/lib/codemirror.js (100%) rename {ethereal => Mist}/assets/muted/lib/go.js (100%) rename {ethereal => Mist}/assets/muted/lib/matchbrackets.js (100%) rename {ethereal => Mist}/assets/muted/muted.js (100%) rename {ethereal => Mist}/assets/net.png (100%) rename {ethereal => Mist}/assets/network.png (100%) rename {ethereal => Mist}/assets/new.png (100%) rename {ethereal => Mist}/assets/pick.png (100%) rename {ethereal => Mist}/assets/qml/QmlApp.qml (100%) rename {ethereal => Mist}/assets/qml/first_run.qml (100%) rename {ethereal => Mist}/assets/qml/muted.qml (100%) rename {ethereal => Mist}/assets/qml/test_app.qml (100%) rename {ethereal => Mist}/assets/qml/transactions.qml (100%) rename {ethereal => Mist}/assets/qml/views/chain.qml (100%) rename {ethereal => Mist}/assets/qml/views/history.qml (100%) rename {ethereal => Mist}/assets/qml/views/info.qml (100%) rename {ethereal => Mist}/assets/qml/views/javascript.qml (100%) rename {ethereal => Mist}/assets/qml/views/pending_tx.qml (100%) rename {ethereal => Mist}/assets/qml/views/transaction.qml (100%) rename {ethereal => Mist}/assets/qml/views/wallet.qml (100%) rename {ethereal => Mist}/assets/qml/wallet.qml (100%) rename {ethereal => Mist}/assets/qml/webapp.qml (100%) rename {ethereal => Mist}/assets/tx.png (100%) rename {ethereal => Mist}/assets/util/test.html (100%) rename {ethereal => Mist}/assets/wallet.png (100%) rename {ethereal => Mist}/bindings.go (100%) rename {ethereal => Mist}/debugger.go (100%) rename {ethereal => Mist}/errors.go (100%) rename {ethereal => Mist}/ext_app.go (100%) rename {ethereal => Mist}/flags.go (100%) rename {ethereal => Mist}/gui.go (100%) rename {ethereal => Mist}/html_container.go (100%) rename {ethereal => Mist}/main.go (100%) rename {ethereal => Mist}/qml_container.go (100%) rename {ethereal => Mist}/ui_lib.go (100%) diff --git a/ethereal/assets/back.png b/Mist/assets/back.png similarity index 100% rename from ethereal/assets/back.png rename to Mist/assets/back.png diff --git a/ethereal/assets/browser.png b/Mist/assets/browser.png similarity index 100% rename from ethereal/assets/browser.png rename to Mist/assets/browser.png diff --git a/ethereal/assets/bug.png b/Mist/assets/bug.png similarity index 100% rename from ethereal/assets/bug.png rename to Mist/assets/bug.png diff --git a/ethereal/assets/close.png b/Mist/assets/close.png similarity index 100% rename from ethereal/assets/close.png rename to Mist/assets/close.png diff --git a/ethereal/assets/debugger/debugger.qml b/Mist/assets/debugger/debugger.qml similarity index 100% rename from ethereal/assets/debugger/debugger.qml rename to Mist/assets/debugger/debugger.qml diff --git a/ethereal/assets/ext/big.js b/Mist/assets/ext/big.js similarity index 100% rename from ethereal/assets/ext/big.js rename to Mist/assets/ext/big.js diff --git a/ethereal/assets/ext/ethereum.js b/Mist/assets/ext/ethereum.js similarity index 100% rename from ethereal/assets/ext/ethereum.js rename to Mist/assets/ext/ethereum.js diff --git a/ethereal/assets/ext/filter.js b/Mist/assets/ext/filter.js similarity index 100% rename from ethereal/assets/ext/filter.js rename to Mist/assets/ext/filter.js diff --git a/ethereal/assets/ext/home.html b/Mist/assets/ext/home.html similarity index 100% rename from ethereal/assets/ext/home.html rename to Mist/assets/ext/home.html diff --git a/ethereal/assets/ext/http.js b/Mist/assets/ext/http.js similarity index 100% rename from ethereal/assets/ext/http.js rename to Mist/assets/ext/http.js diff --git a/ethereal/assets/ext/pre.js b/Mist/assets/ext/pre.js similarity index 100% rename from ethereal/assets/ext/pre.js rename to Mist/assets/ext/pre.js diff --git a/ethereal/assets/ext/string.js b/Mist/assets/ext/string.js similarity index 100% rename from ethereal/assets/ext/string.js rename to Mist/assets/ext/string.js diff --git a/ethereal/assets/ext/test.html b/Mist/assets/ext/test.html similarity index 100% rename from ethereal/assets/ext/test.html rename to Mist/assets/ext/test.html diff --git a/ethereal/assets/facet.png b/Mist/assets/facet.png similarity index 100% rename from ethereal/assets/facet.png rename to Mist/assets/facet.png diff --git a/ethereal/assets/heart.png b/Mist/assets/heart.png similarity index 100% rename from ethereal/assets/heart.png rename to Mist/assets/heart.png diff --git a/ethereal/assets/muted/codemirror.css b/Mist/assets/muted/codemirror.css similarity index 100% rename from ethereal/assets/muted/codemirror.css rename to Mist/assets/muted/codemirror.css diff --git a/ethereal/assets/muted/debugger.html b/Mist/assets/muted/debugger.html similarity index 100% rename from ethereal/assets/muted/debugger.html rename to Mist/assets/muted/debugger.html diff --git a/ethereal/assets/muted/eclipse.css b/Mist/assets/muted/eclipse.css similarity index 100% rename from ethereal/assets/muted/eclipse.css rename to Mist/assets/muted/eclipse.css diff --git a/ethereal/assets/muted/index.html b/Mist/assets/muted/index.html similarity index 100% rename from ethereal/assets/muted/index.html rename to Mist/assets/muted/index.html diff --git a/ethereal/assets/muted/lib/codemirror.js b/Mist/assets/muted/lib/codemirror.js similarity index 100% rename from ethereal/assets/muted/lib/codemirror.js rename to Mist/assets/muted/lib/codemirror.js diff --git a/ethereal/assets/muted/lib/go.js b/Mist/assets/muted/lib/go.js similarity index 100% rename from ethereal/assets/muted/lib/go.js rename to Mist/assets/muted/lib/go.js diff --git a/ethereal/assets/muted/lib/matchbrackets.js b/Mist/assets/muted/lib/matchbrackets.js similarity index 100% rename from ethereal/assets/muted/lib/matchbrackets.js rename to Mist/assets/muted/lib/matchbrackets.js diff --git a/ethereal/assets/muted/muted.js b/Mist/assets/muted/muted.js similarity index 100% rename from ethereal/assets/muted/muted.js rename to Mist/assets/muted/muted.js diff --git a/ethereal/assets/net.png b/Mist/assets/net.png similarity index 100% rename from ethereal/assets/net.png rename to Mist/assets/net.png diff --git a/ethereal/assets/network.png b/Mist/assets/network.png similarity index 100% rename from ethereal/assets/network.png rename to Mist/assets/network.png diff --git a/ethereal/assets/new.png b/Mist/assets/new.png similarity index 100% rename from ethereal/assets/new.png rename to Mist/assets/new.png diff --git a/ethereal/assets/pick.png b/Mist/assets/pick.png similarity index 100% rename from ethereal/assets/pick.png rename to Mist/assets/pick.png diff --git a/ethereal/assets/qml/QmlApp.qml b/Mist/assets/qml/QmlApp.qml similarity index 100% rename from ethereal/assets/qml/QmlApp.qml rename to Mist/assets/qml/QmlApp.qml diff --git a/ethereal/assets/qml/first_run.qml b/Mist/assets/qml/first_run.qml similarity index 100% rename from ethereal/assets/qml/first_run.qml rename to Mist/assets/qml/first_run.qml diff --git a/ethereal/assets/qml/muted.qml b/Mist/assets/qml/muted.qml similarity index 100% rename from ethereal/assets/qml/muted.qml rename to Mist/assets/qml/muted.qml diff --git a/ethereal/assets/qml/test_app.qml b/Mist/assets/qml/test_app.qml similarity index 100% rename from ethereal/assets/qml/test_app.qml rename to Mist/assets/qml/test_app.qml diff --git a/ethereal/assets/qml/transactions.qml b/Mist/assets/qml/transactions.qml similarity index 100% rename from ethereal/assets/qml/transactions.qml rename to Mist/assets/qml/transactions.qml diff --git a/ethereal/assets/qml/views/chain.qml b/Mist/assets/qml/views/chain.qml similarity index 100% rename from ethereal/assets/qml/views/chain.qml rename to Mist/assets/qml/views/chain.qml diff --git a/ethereal/assets/qml/views/history.qml b/Mist/assets/qml/views/history.qml similarity index 100% rename from ethereal/assets/qml/views/history.qml rename to Mist/assets/qml/views/history.qml diff --git a/ethereal/assets/qml/views/info.qml b/Mist/assets/qml/views/info.qml similarity index 100% rename from ethereal/assets/qml/views/info.qml rename to Mist/assets/qml/views/info.qml diff --git a/ethereal/assets/qml/views/javascript.qml b/Mist/assets/qml/views/javascript.qml similarity index 100% rename from ethereal/assets/qml/views/javascript.qml rename to Mist/assets/qml/views/javascript.qml diff --git a/ethereal/assets/qml/views/pending_tx.qml b/Mist/assets/qml/views/pending_tx.qml similarity index 100% rename from ethereal/assets/qml/views/pending_tx.qml rename to Mist/assets/qml/views/pending_tx.qml diff --git a/ethereal/assets/qml/views/transaction.qml b/Mist/assets/qml/views/transaction.qml similarity index 100% rename from ethereal/assets/qml/views/transaction.qml rename to Mist/assets/qml/views/transaction.qml diff --git a/ethereal/assets/qml/views/wallet.qml b/Mist/assets/qml/views/wallet.qml similarity index 100% rename from ethereal/assets/qml/views/wallet.qml rename to Mist/assets/qml/views/wallet.qml diff --git a/ethereal/assets/qml/wallet.qml b/Mist/assets/qml/wallet.qml similarity index 100% rename from ethereal/assets/qml/wallet.qml rename to Mist/assets/qml/wallet.qml diff --git a/ethereal/assets/qml/webapp.qml b/Mist/assets/qml/webapp.qml similarity index 100% rename from ethereal/assets/qml/webapp.qml rename to Mist/assets/qml/webapp.qml diff --git a/ethereal/assets/tx.png b/Mist/assets/tx.png similarity index 100% rename from ethereal/assets/tx.png rename to Mist/assets/tx.png diff --git a/ethereal/assets/util/test.html b/Mist/assets/util/test.html similarity index 100% rename from ethereal/assets/util/test.html rename to Mist/assets/util/test.html diff --git a/ethereal/assets/wallet.png b/Mist/assets/wallet.png similarity index 100% rename from ethereal/assets/wallet.png rename to Mist/assets/wallet.png diff --git a/ethereal/bindings.go b/Mist/bindings.go similarity index 100% rename from ethereal/bindings.go rename to Mist/bindings.go diff --git a/ethereal/debugger.go b/Mist/debugger.go similarity index 100% rename from ethereal/debugger.go rename to Mist/debugger.go diff --git a/ethereal/errors.go b/Mist/errors.go similarity index 100% rename from ethereal/errors.go rename to Mist/errors.go diff --git a/ethereal/ext_app.go b/Mist/ext_app.go similarity index 100% rename from ethereal/ext_app.go rename to Mist/ext_app.go diff --git a/ethereal/flags.go b/Mist/flags.go similarity index 100% rename from ethereal/flags.go rename to Mist/flags.go diff --git a/ethereal/gui.go b/Mist/gui.go similarity index 100% rename from ethereal/gui.go rename to Mist/gui.go diff --git a/ethereal/html_container.go b/Mist/html_container.go similarity index 100% rename from ethereal/html_container.go rename to Mist/html_container.go diff --git a/ethereal/main.go b/Mist/main.go similarity index 100% rename from ethereal/main.go rename to Mist/main.go diff --git a/ethereal/qml_container.go b/Mist/qml_container.go similarity index 100% rename from ethereal/qml_container.go rename to Mist/qml_container.go diff --git a/ethereal/ui_lib.go b/Mist/ui_lib.go similarity index 100% rename from ethereal/ui_lib.go rename to Mist/ui_lib.go From 01863ebff06d620c9d3a8cf9195d72caeb11dc19 Mon Sep 17 00:00:00 2001 From: obscuren Date: Wed, 17 Sep 2014 15:58:44 +0200 Subject: [PATCH 21/39] Rename and changed peer window --- Mist/assets/qml/wallet.qml | 43 +++++++++++++++++++++++++++----- Mist/flags.go | 6 ++--- Mist/gui.go | 8 ++++-- Mist/main.go | 4 +-- Mist/ui_lib.go | 4 +++ javascript/javascript_runtime.go | 2 +- utils/cmd.go | 4 +-- 7 files changed, 55 insertions(+), 16 deletions(-) diff --git a/Mist/assets/qml/wallet.qml b/Mist/assets/qml/wallet.qml index 4867c683329a..fc3e7a38393f 100644 --- a/Mist/assets/qml/wallet.qml +++ b/Mist/assets/qml/wallet.qml @@ -19,7 +19,7 @@ ApplicationWindow { height: 600 minimumHeight: 300 - title: "Ether Browser" + title: "Mist" // This signal is used by the filter API. The filter API connects using this signal handler from // the different QML files and plugins. @@ -853,16 +853,20 @@ ApplicationWindow { Window { id: addPeerWin visible: false - minimumWidth: 230 - maximumWidth: 230 + minimumWidth: 300 + maximumWidth: 300 maximumHeight: 50 minimumHeight: 50 + title: "Add peer" + /* TextField { id: addrField anchors.verticalCenter: parent.verticalCenter anchors.left: parent.left + anchors.right: addPeerButton.left anchors.leftMargin: 10 + anchors.rightMargin: 10 placeholderText: "address:port" text: "54.76.56.74:30303" onAccepted: { @@ -870,13 +874,40 @@ ApplicationWindow { addPeerWin.visible = false } } + */ + ComboBox { + id: addrField + anchors.verticalCenter: parent.verticalCenter + anchors.left: parent.left + anchors.right: addPeerButton.left + anchors.leftMargin: 10 + anchors.rightMargin: 10 + onAccepted: { + eth.connectToPeer(addrField.currentText) + addPeerWin.visible = false + } + + editable: true + model: ListModel { id: pastPeers } + + Component.onCompleted: { + var ips = eth.pastPeers() + for(var i = 0; i < ips.length; i++) { + pastPeers.append({text: ips.get(i)}) + } + + pastPeers.insert(0, {text: "54.76.56.74:30303"}) + } + } + Button { - anchors.left: addrField.right + id: addPeerButton + anchors.right: parent.right anchors.verticalCenter: parent.verticalCenter - anchors.leftMargin: 5 + anchors.rightMargin: 10 text: "Add" onClicked: { - eth.connectToPeer(addrField.text) + eth.connectToPeer(addrField.currentText) addPeerWin.visible = false } } diff --git a/Mist/flags.go b/Mist/flags.go index 382f093aa4d6..388280b8c748 100644 --- a/Mist/flags.go +++ b/Mist/flags.go @@ -44,7 +44,7 @@ func defaultAssetPath() string { // assume a debug build and use the source directory as // asset directory. pwd, _ := os.Getwd() - if pwd == path.Join(os.Getenv("GOPATH"), "src", "github.com", "ethereum", "go-ethereum", "ethereal") { + if pwd == path.Join(os.Getenv("GOPATH"), "src", "github.com", "ethereum", "go-ethereum", "Mist") { assetPath = path.Join(pwd, "assets") } else { switch runtime.GOOS { @@ -53,7 +53,7 @@ func defaultAssetPath() string { exedir, _ := osext.ExecutableFolder() assetPath = filepath.Join(exedir, "../Resources") case "linux": - assetPath = "/usr/share/ethereal" + assetPath = "/usr/share/mist" case "windows": assetPath = "./assets" default: @@ -64,7 +64,7 @@ func defaultAssetPath() string { } func defaultDataDir() string { usr, _ := user.Current() - return path.Join(usr.HomeDir, ".ethereal") + return path.Join(usr.HomeDir, ".mist") } var defaultConfigFile = path.Join(defaultDataDir(), "conf.ini") diff --git a/Mist/gui.go b/Mist/gui.go index 208157d69c71..538719703c22 100644 --- a/Mist/gui.go +++ b/Mist/gui.go @@ -491,7 +491,9 @@ func (gui *Gui) setStatsPane() { runtime.ReadMemStats(&memStats) statsPane := gui.getObjectByName("statsPane") - statsPane.Set("text", fmt.Sprintf(`###### Ethereal 0.6.4 (%s) ####### + statsPane.Set("text", fmt.Sprintf(`###### Mist 0.6.4 (%s) ####### + +eth %d (p2p = %d) CPU: # %d Goroutines: # %d @@ -502,7 +504,9 @@ Heap Alloc: %d CGNext: %x NumGC: %d -`, runtime.Version(), runtime.NumCPU, runtime.NumGoroutine(), runtime.NumCgoCall(), +`, runtime.Version(), + eth.ProtocolVersion, eth.P2PVersion, + runtime.NumCPU, runtime.NumGoroutine(), runtime.NumCgoCall(), memStats.Alloc, memStats.HeapAlloc, memStats.NextGC, memStats.NumGC, )) diff --git a/Mist/main.go b/Mist/main.go index dff0abbb7a82..6e4554352776 100644 --- a/Mist/main.go +++ b/Mist/main.go @@ -11,8 +11,8 @@ import ( ) const ( - ClientIdentifier = "Ethereal" - Version = "0.6.6" + ClientIdentifier = "Mist" + Version = "0.6.7" ) var ethereum *eth.Ethereum diff --git a/Mist/ui_lib.go b/Mist/ui_lib.go index 4e1f7f45c4cd..1434e28d0659 100644 --- a/Mist/ui_lib.go +++ b/Mist/ui_lib.go @@ -72,6 +72,10 @@ func (self *UiLib) LookupDomain(domain string) string { } } +func (self *UiLib) PastPeers() *ethutil.List { + return ethutil.NewList(eth.PastPeers()) +} + func (self *UiLib) ImportTx(rlpTx string) { tx := ethchain.NewTransactionFromBytes(ethutil.Hex2Bytes(rlpTx)) self.eth.TxPool().QueueTransaction(tx) diff --git a/javascript/javascript_runtime.go b/javascript/javascript_runtime.go index c794c32a8a6d..94301b859a24 100644 --- a/javascript/javascript_runtime.go +++ b/javascript/javascript_runtime.go @@ -42,7 +42,7 @@ func (jsre *JSRE) LoadExtFile(path string) { } func (jsre *JSRE) LoadIntFile(file string) { - assetPath := path.Join(os.Getenv("GOPATH"), "src", "github.com", "ethereum", "go-ethereum", "ethereal", "assets", "ext") + assetPath := path.Join(os.Getenv("GOPATH"), "src", "github.com", "ethereum", "go-ethereum", "Mist", "assets", "ext") jsre.LoadExtFile(path.Join(assetPath, file)) } diff --git a/utils/cmd.go b/utils/cmd.go index 83f3ec0b6846..da3cac444f16 100644 --- a/utils/cmd.go +++ b/utils/cmd.go @@ -189,7 +189,7 @@ func DefaultAssetPath() string { // assume a debug build and use the source directory as // asset directory. pwd, _ := os.Getwd() - if pwd == path.Join(os.Getenv("GOPATH"), "src", "github.com", "ethereum", "go-ethereum", "ethereal") { + if pwd == path.Join(os.Getenv("GOPATH"), "src", "github.com", "ethereum", "go-ethereum", "Mist") { assetPath = path.Join(pwd, "assets") } else { switch runtime.GOOS { @@ -198,7 +198,7 @@ func DefaultAssetPath() string { exedir, _ := osext.ExecutableFolder() assetPath = filepath.Join(exedir, "../Resources") case "linux": - assetPath = "/usr/share/ethereal" + assetPath = "/usr/share/mist" case "windows": assetPath = "./assets" default: From b27100c8fc48c819c9e6a6b76fcc50cfe118cedb Mon Sep 17 00:00:00 2001 From: JoeG Date: Thu, 18 Sep 2014 01:03:55 +0100 Subject: [PATCH 22/39] Fix to display Mist menu bar on linux --- Mist/assets/qml/wallet.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mist/assets/qml/wallet.qml b/Mist/assets/qml/wallet.qml index fc3e7a38393f..50cac05204b8 100644 --- a/Mist/assets/qml/wallet.qml +++ b/Mist/assets/qml/wallet.qml @@ -101,7 +101,7 @@ ApplicationWindow { } } - MenuBar { + menuBar: MenuBar { Menu { title: "File" MenuItem { From 728005722853028348909e1d458593415cb52660 Mon Sep 17 00:00:00 2001 From: obscuren Date: Thu, 18 Sep 2014 11:27:55 +0200 Subject: [PATCH 23/39] Minor visual updates --- Mist/assets/qml/views/wallet.qml | 2 +- Mist/assets/qml/wallet.qml | 1789 +++++++++++++++--------------- javascript/types.go | 4 + 3 files changed, 889 insertions(+), 906 deletions(-) diff --git a/Mist/assets/qml/views/wallet.qml b/Mist/assets/qml/views/wallet.qml index 2a766bb5b4e8..fbe1dfd0e494 100644 --- a/Mist/assets/qml/views/wallet.qml +++ b/Mist/assets/qml/views/wallet.qml @@ -9,7 +9,7 @@ import Ethereum 1.0 Rectangle { id: root property var title: "Wallet" - property var iconSource: "../wallet.png" + property var iconSource: "../facet.png" property var menuItem objectName: "walletView" diff --git a/Mist/assets/qml/wallet.qml b/Mist/assets/qml/wallet.qml index fc3e7a38393f..734c85f8b188 100644 --- a/Mist/assets/qml/wallet.qml +++ b/Mist/assets/qml/wallet.qml @@ -10,909 +10,888 @@ import "../ext/filter.js" as Eth import "../ext/http.js" as Http ApplicationWindow { - id: root - - property alias miningButtonText: miningButton.text - property var ethx : Eth.ethx - - width: 900 - height: 600 - minimumHeight: 300 - - title: "Mist" - - // 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); - function invokeFilterCallback(data, receiverSeed) { - //var messages = JSON.parse(data) - // Signal handler - message(data, receiverSeed); - } - - TextField { - id: copyElementHax - visible: false - } - - function copyToClipboard(text) { - copyElementHax.text = text - copyElementHax.selectAll() - copyElementHax.copy() - } - - // Takes care of loading all default plugins - Component.onCompleted: { - addPlugin("./views/wallet.qml", {noAdd: true, section: "ethereum", active: true}); - addPlugin("./webapp.qml", {noAdd: true, section: "ethereum", active: true}); - - addPlugin("./views/transaction.qml", {noAdd: true, section: "legacy"}); - addPlugin("./views/chain.qml", {noAdd: true, section: "legacy"}); - addPlugin("./views/info.qml", {noAdd: true, section: "legacy"}); - addPlugin("./views/pending_tx.qml", {noAdd: true, section: "legacy"}); - addPlugin("./views/javascript.qml", {noAdd: true, section: "legacy"}); - - // Call the ready handler - gui.done(); - } - - function addViews(view, path, options) { - var views = mainSplit.addComponent(view, options) - views.menuItem.path = path - - mainSplit.views.push(views); - - if(!options.noAdd) { - gui.addPlugin(path) - } - - return views - } - - function addPlugin(path, options) { - try { - if(typeof(path) === "string" && /^https?/.test(path)) { - console.log('load http') - Http.request(path, function(o) { - if(o.status === 200) { - var view = Qt.createQmlObject(o.responseText, mainView, path) - addViews(view, path, options) - } - }) - - return - } - - var component = Qt.createComponent(path); - if(component.status != Component.Ready) { - if(component.status == Component.Error) { - ethx.note("error: ", component.errorString()); - } - - return - } - - var view = mainView.createView(component, options) - var views = addViews(view, path, options) - - return views.view - } catch(e) { - ethx.note(e) - } - } - - MenuBar { - Menu { - title: "File" - MenuItem { - text: "Import App" - shortcut: "Ctrl+o" - onTriggered: { - generalFileDialog.show(true, importApp) - } - } - - /* - MenuItem { - text: "Browser" - onTriggered: eth.openBrowser() - } - */ - - MenuItem { - text: "Add plugin" - onTriggered: { - generalFileDialog.show(true, function(path) { - addPlugin(path, {canClose: true, section: "apps"}) - }) - } - } - - MenuSeparator {} - - MenuItem { - text: "Import key" - shortcut: "Ctrl+i" - onTriggered: { - generalFileDialog.show(true, function(path) { - gui.importKey(path) - }) - } - } - - MenuItem { - text: "Export keys" - shortcut: "Ctrl+e" - onTriggered: { - generalFileDialog.show(false, function(path) { - }) - } - } - - } - - Menu { - title: "Developer" - MenuItem { - text: "Debugger" - shortcut: "Ctrl+d" - onTriggered: eth.startDebugger() - } - - MenuItem { - text: "Import Tx" - onTriggered: { - txImportDialog.visible = true - } - } - - MenuItem { - text: "Run JS file" - onTriggered: { - generalFileDialog.show(true, function(path) { - eth.evalJavascriptFile(path) - }) - } - } - - MenuItem { - text: "Dump state" - onTriggered: { - generalFileDialog.show(false, function(path) { - // Empty hash for latest - gui.dumpState("", path) - }) - } - } - - MenuSeparator {} - - MenuItem { - id: miningSpeed - text: "Mining: Turbo" - onTriggered: { - gui.toggleTurboMining() - if(text == "Mining: Turbo") { - text = "Mining: Normal"; - } else { - text = "Mining: Turbo"; - } - } - } - } - - Menu { - title: "Network" - MenuItem { - text: "Add Peer" - shortcut: "Ctrl+p" - onTriggered: { - addPeerWin.visible = true - } - } - MenuItem { - text: "Show Peers" - shortcut: "Ctrl+e" - onTriggered: { - peerWindow.visible = true - } - } - } - - Menu { - title: "Help" - MenuItem { - text: "About" - onTriggered: { - aboutWin.visible = true - } - } - } - - Menu { - title: "GLOBAL SHORTCUTS" - visible: false - MenuItem { - visible: false - shortcut: "Ctrl+l" - onTriggered: { - url.focus = true - } - } - } - } - - statusBar: StatusBar { - height: 32 - RowLayout { - Button { - id: miningButton - text: "Start Mining" - onClicked: { - gui.toggleMining() - } - } - - /* - Button { - id: importAppButton - text: "Browser" - onClicked: { - eth.openBrowser() - } - } - */ - - RowLayout { - Label { - id: walletValueLabel - - font.pixelSize: 10 - styleColor: "#797979" - } - } - } - - Label { - y: 6 - objectName: "miningLabel" - visible: true - font.pixelSize: 10 - anchors.right: lastBlockLabel.left - anchors.rightMargin: 5 - } - - Label { - y: 6 - id: lastBlockLabel - objectName: "lastBlockLabel" - visible: true - text: "" - font.pixelSize: 10 - 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 - } - - Label { - id: peerLabel - font.pixelSize: 8 - text: "0 / 0" - } - Image { - id: peerImage - width: 10; height: 10 - source: "../network.png" - } - } - } - - - property var blockModel: ListModel { - id: blockModel - } - - SplitView { - property var views: []; - - id: mainSplit - anchors.fill: parent - resizing: false - - function setView(view, menu) { - for(var i = 0; i < views.length; i++) { - views[i].view.visible = false - views[i].menuItem.setSelection(false) - } - view.visible = true - - //menu.border.color = "#CCCCCC" - //menu.color = "#FFFFFFFF" - menu.setSelection(true) - } - - function addComponent(view, options) { - view.visible = false - view.anchors.fill = mainView - - if( !view.hasOwnProperty("iconSource") ) { - console.log("Could not load plugin. Property 'iconSourc' not found on view."); - return; - } - - var menuItem = menu.createMenuItem(view.iconSource, view, options); - if( view.hasOwnProperty("menuItem") ) { - view.menuItem = menuItem; - } - - if( view.hasOwnProperty("onReady") ) { - view.onReady.call(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" - - Component { - id: menuItemTemplate - Rectangle { - id: menuItem - property var view; - property var path; - - property alias title: label.text - property alias icon: icon.source - property alias secondaryTitle: secondary.text - function setSelection(on) { - sel.visible = on - } - - width: 176 - height: 28 - color: "#00000000" - - anchors { - left: parent.left - leftMargin: 4 - } - - Rectangle { - id: sel - visible: false - anchors.fill: parent - color: "#00000000" - Rectangle { - id: r - anchors.fill: parent - border.color: "#CCCCCC" - border.width: 1 - radius: 5 - color: "#FFFFFFFF" - } - Rectangle { - anchors { - top: r.top - bottom: r.bottom - right: r.right - } - width: 10 - color: "#FFFFFFFF" - - Rectangle { - anchors { - left: parent.left - right: parent.right - top: parent.top - } - height: 1 - color: "#CCCCCC" - } - - Rectangle { - anchors { - left: parent.left - right: parent.right - bottom: parent.bottom - } - height: 1 - color: "#CCCCCC" - } - } - } - - MouseArea { - anchors.fill: parent - onClicked: { - mainSplit.setView(view, menuItem) - } - } - - Image { - id: icon - height: 20 - width: 20 - anchors { - left: parent.left - verticalCenter: parent.verticalCenter - leftMargin: 3 - } - MouseArea { - anchors.fill: parent - onClicked: { - menuItem.closeApp() - } - } - } - - Text { - id: label - anchors { - left: icon.right - verticalCenter: parent.verticalCenter - leftMargin: 3 - } - - color: "#0D0A01" - font.pixelSize: 12 - } - - 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.iconSource - /* - 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: "APPS" - font.bold: true - anchors { - left: parent.left - leftMargin: 5 - } - color: "#888888" - } - - ColumnLayout { - id: menuApps - spacing: 3 - anchors { - left: parent.left - right: parent.right - } - } - - 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 { - anchors.right: parent.right - anchors.left: menu.right - anchors.bottom: parent.bottom - anchors.top: parent.top - color: "#00000000" - - Rectangle { - id: urlPane - height: 40 - color: "#00000000" - anchors { - left: parent.left - right: parent.right - leftMargin: 5 - rightMargin: 5 - top: parent.top - topMargin: 5 - } - TextField { - id: url - objectName: "url" - placeholderText: "DApp URL" - anchors { - left: parent.left - right: parent.right - top: parent.top - topMargin: 5 - rightMargin: 5 - leftMargin: 5 - } - - Keys.onReturnPressed: { - addPlugin(this.text, {canClose: true, section: "apps"}) - } - } - - } - - // Border - Rectangle { - id: divider - anchors { - left: parent.left - right: parent.right - top: urlPane.bottom - } - z: -1 - height: 1 - color: "#CCCCCC" - } - - Rectangle { - id: mainView - - - anchors.right: parent.right - anchors.left: parent.left - anchors.bottom: parent.bottom - anchors.top: divider.bottom - - function createView(component) { - var view = component.createObject(mainView) - - return view; - } - } - } - } - - - /****************** - * Dialogs - *****************/ - FileDialog { - id: generalFileDialog - property var callback; - onAccepted: { - var path = this.fileUrl.toString(); - callback.call(this, path); - } - - function show(selectExisting, callback) { - generalFileDialog.callback = callback; - generalFileDialog.selectExisting = selectExisting; - - this.open(); - } - } - - - /****************** - * 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 setWalletValue(value) { - walletValueLabel.text = value - } - - function loadPlugin(name) { - console.log("Loading plugin" + name) - var view = 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 - Aitne


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: 300 - maximumWidth: 300 - maximumHeight: 50 - minimumHeight: 50 - title: "Add peer" - - /* - TextField { - id: addrField - anchors.verticalCenter: parent.verticalCenter - anchors.left: parent.left - anchors.right: addPeerButton.left - anchors.leftMargin: 10 - anchors.rightMargin: 10 - placeholderText: "address:port" - text: "54.76.56.74:30303" - onAccepted: { - eth.connectToPeer(addrField.text) - addPeerWin.visible = false - } - } - */ - ComboBox { - id: addrField - anchors.verticalCenter: parent.verticalCenter - anchors.left: parent.left - anchors.right: addPeerButton.left - anchors.leftMargin: 10 - anchors.rightMargin: 10 - onAccepted: { - eth.connectToPeer(addrField.currentText) - addPeerWin.visible = false - } - - editable: true - model: ListModel { id: pastPeers } - - Component.onCompleted: { - var ips = eth.pastPeers() - for(var i = 0; i < ips.length; i++) { - pastPeers.append({text: ips.get(i)}) - } - - pastPeers.insert(0, {text: "54.76.56.74:30303"}) - } - } - - Button { - id: addPeerButton - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - anchors.rightMargin: 10 - text: "Add" - onClicked: { - eth.connectToPeer(addrField.currentText) - addPeerWin.visible = false - } - } - Component.onCompleted: { - addrField.focus = true - } - } + id: root + + property alias miningButtonText: miningButton.text + property var ethx : Eth.ethx + + width: 900 + height: 600 + minimumHeight: 300 + + title: "Mist" + + // 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); + function invokeFilterCallback(data, receiverSeed) { + //var messages = JSON.parse(data) + // Signal handler + message(data, receiverSeed); + } + + TextField { + id: copyElementHax + visible: false + } + + function copyToClipboard(text) { + copyElementHax.text = text + copyElementHax.selectAll() + copyElementHax.copy() + } + + // Takes care of loading all default plugins + Component.onCompleted: { + addPlugin("./views/wallet.qml", {noAdd: true, close: false, section: "ethereum", active: true}); + addPlugin("./webapp.qml", {noAdd: true, close: false, section: "ethereum", active: true}); + + addPlugin("./views/transaction.qml", {noAdd: true, close: false, section: "legacy"}); + addPlugin("./views/chain.qml", {noAdd: true, close: false, section: "legacy"}); + addPlugin("./views/info.qml", {noAdd: true, close: false, section: "legacy"}); + addPlugin("./views/pending_tx.qml", {noAdd: true, close: false, section: "legacy"}); + addPlugin("./views/javascript.qml", {noAdd: true, close: false, section: "legacy"}); + + // Call the ready handler + gui.done(); + } + + function addViews(view, path, options) { + var views = mainSplit.addComponent(view, options) + views.menuItem.path = path + + mainSplit.views.push(views); + + if(!options.noAdd) { + gui.addPlugin(path) + } + + return views + } + + function addPlugin(path, options) { + try { + if(typeof(path) === "string" && /^https?/.test(path)) { + console.log('load http') + Http.request(path, function(o) { + if(o.status === 200) { + var view = Qt.createQmlObject(o.responseText, mainView, path) + addViews(view, path, options) + } + }) + + return + } + + var component = Qt.createComponent(path); + if(component.status != Component.Ready) { + if(component.status == Component.Error) { + ethx.note("error: ", component.errorString()); + } + + return + } + + var view = mainView.createView(component, options) + var views = addViews(view, path, options) + + return views.view + } catch(e) { + ethx.note(e) + } + } + + MenuBar { + Menu { + title: "File" + MenuItem { + text: "Import App" + shortcut: "Ctrl+o" + onTriggered: { + generalFileDialog.show(true, importApp) + } + } + + /* + MenuItem { + text: "Browser" + onTriggered: eth.openBrowser() + } + */ + + MenuItem { + text: "Add plugin" + onTriggered: { + generalFileDialog.show(true, function(path) { + addPlugin(path, {close: true, section: "apps"}) + }) + } + } + + MenuSeparator {} + + MenuItem { + text: "Import key" + shortcut: "Ctrl+i" + onTriggered: { + generalFileDialog.show(true, function(path) { + gui.importKey(path) + }) + } + } + + MenuItem { + text: "Export keys" + shortcut: "Ctrl+e" + onTriggered: { + generalFileDialog.show(false, function(path) { + }) + } + } + + } + + Menu { + title: "Developer" + MenuItem { + text: "Debugger" + shortcut: "Ctrl+d" + onTriggered: eth.startDebugger() + } + + MenuItem { + text: "Import Tx" + onTriggered: { + txImportDialog.visible = true + } + } + + MenuItem { + text: "Run JS file" + onTriggered: { + generalFileDialog.show(true, function(path) { + eth.evalJavascriptFile(path) + }) + } + } + + MenuItem { + text: "Dump state" + onTriggered: { + generalFileDialog.show(false, function(path) { + // Empty hash for latest + gui.dumpState("", path) + }) + } + } + + MenuSeparator {} + + MenuItem { + id: miningSpeed + text: "Mining: Turbo" + onTriggered: { + gui.toggleTurboMining() + if(text == "Mining: Turbo") { + text = "Mining: Normal"; + } else { + text = "Mining: Turbo"; + } + } + } + } + + Menu { + title: "Network" + MenuItem { + text: "Add Peer" + shortcut: "Ctrl+p" + onTriggered: { + addPeerWin.visible = true + } + } + MenuItem { + text: "Show Peers" + shortcut: "Ctrl+e" + onTriggered: { + peerWindow.visible = true + } + } + } + + Menu { + title: "Help" + MenuItem { + text: "About" + onTriggered: { + aboutWin.visible = true + } + } + } + + Menu { + title: "GLOBAL SHORTCUTS" + visible: false + MenuItem { + visible: false + shortcut: "Ctrl+l" + onTriggered: { + url.focus = true + } + } + } + } + + statusBar: StatusBar { + height: 32 + RowLayout { + Button { + id: miningButton + text: "Start Mining" + onClicked: { + gui.toggleMining() + } + } + + /* + Button { + id: importAppButton + text: "Browser" + onClicked: { + eth.openBrowser() + } + } + */ + + RowLayout { + Label { + id: walletValueLabel + + font.pixelSize: 10 + styleColor: "#797979" + } + } + } + + Label { + y: 6 + objectName: "miningLabel" + visible: true + font.pixelSize: 10 + anchors.right: lastBlockLabel.left + anchors.rightMargin: 5 + } + + Label { + y: 6 + id: lastBlockLabel + objectName: "lastBlockLabel" + visible: true + text: "" + font.pixelSize: 10 + 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 + } + + Label { + id: peerLabel + font.pixelSize: 8 + text: "0 / 0" + } + Image { + id: peerImage + width: 10; height: 10 + source: "../network.png" + } + } + } + + + property var blockModel: ListModel { + id: blockModel + } + + SplitView { + property var views: []; + + id: mainSplit + anchors.fill: parent + resizing: false + + function setView(view, menu) { + for(var i = 0; i < views.length; i++) { + views[i].view.visible = false + views[i].menuItem.setSelection(false) + } + view.visible = true + + //menu.border.color = "#CCCCCC" + //menu.color = "#FFFFFFFF" + menu.setSelection(true) + } + + function addComponent(view, options) { + view.visible = false + view.anchors.fill = mainView + + if( !view.hasOwnProperty("iconSource") ) { + console.log("Could not load plugin. Property 'iconSourc' not found on view."); + return; + } + + var menuItem = menu.createMenuItem(view.iconSource, view, options); + if( view.hasOwnProperty("menuItem") ) { + view.menuItem = menuItem; + } + + if( view.hasOwnProperty("onReady") ) { + view.onReady.call(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" + + Component { + id: menuItemTemplate + Rectangle { + id: menuItem + property var view; + property var path; + property var closable; + + property alias title: label.text + property alias icon: icon.source + property alias secondaryTitle: secondary.text + function setSelection(on) { + sel.visible = on + } + + width: 176 + height: 28 + color: "#00000000" + + anchors { + left: parent.left + leftMargin: 4 + } + + Rectangle { + id: sel + visible: false + anchors.fill: parent + color: "#00000000" + Rectangle { + id: r + anchors.fill: parent + border.color: "#CCCCCC" + border.width: 1 + radius: 5 + color: "#FFFFFFFF" + } + Rectangle { + anchors { + top: r.top + bottom: r.bottom + right: r.right + } + width: 10 + color: "#FFFFFFFF" + + Rectangle { + anchors { + left: parent.left + right: parent.right + top: parent.top + } + height: 1 + color: "#CCCCCC" + } + + Rectangle { + anchors { + left: parent.left + right: parent.right + bottom: parent.bottom + } + height: 1 + color: "#CCCCCC" + } + } + } + + MouseArea { + anchors.fill: parent + onClicked: { + mainSplit.setView(view, menuItem) + } + } + + Image { + id: icon + height: 20 + width: 20 + anchors { + left: parent.left + verticalCenter: parent.verticalCenter + leftMargin: 3 + } + MouseArea { + anchors.fill: parent + onClicked: { + menuItem.closeApp() + } + } + } + + Text { + id: label + anchors { + left: icon.right + verticalCenter: parent.verticalCenter + leftMargin: 3 + } + + color: "#0D0A01" + font.pixelSize: 12 + } + + Text { + id: secondary + anchors { + right: parent.right + rightMargin: 8 + verticalCenter: parent.verticalCenter + } + color: "#AEADBE" + font.pixelSize: 12 + } + + + function closeApp() { + if(!this.closable) { return; } + + 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.iconSource + comp.closable = options.close; + + return comp + } + + 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: "APPS" + font.bold: true + anchors { + left: parent.left + leftMargin: 5 + } + color: "#888888" + } + + ColumnLayout { + id: menuApps + spacing: 3 + anchors { + left: parent.left + right: parent.right + } + } + + 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 { + anchors.right: parent.right + anchors.left: menu.right + anchors.bottom: parent.bottom + anchors.top: parent.top + color: "#00000000" + + Rectangle { + id: urlPane + height: 40 + color: "#00000000" + anchors { + left: parent.left + right: parent.right + leftMargin: 5 + rightMargin: 5 + top: parent.top + topMargin: 5 + } + TextField { + id: url + objectName: "url" + placeholderText: "DApp URL" + anchors { + left: parent.left + right: parent.right + top: parent.top + topMargin: 5 + rightMargin: 5 + leftMargin: 5 + } + + Keys.onReturnPressed: { + addPlugin(this.text, {close: true, section: "apps"}) + } + } + + } + + // Border + Rectangle { + id: divider + anchors { + left: parent.left + right: parent.right + top: urlPane.bottom + } + z: -1 + height: 1 + color: "#CCCCCC" + } + + Rectangle { + id: mainView + + + anchors.right: parent.right + anchors.left: parent.left + anchors.bottom: parent.bottom + anchors.top: divider.bottom + + function createView(component) { + var view = component.createObject(mainView) + + return view; + } + } + } + } + + + /****************** + * Dialogs + *****************/ + FileDialog { + id: generalFileDialog + property var callback; + onAccepted: { + var path = this.fileUrl.toString(); + callback.call(this, path); + } + + function show(selectExisting, callback) { + generalFileDialog.callback = callback; + generalFileDialog.selectExisting = selectExisting; + + this.open(); + } + } + + + /****************** + * Wallet functions + *****************/ + function importApp(path) { + var ext = path.split('.').pop() + if(ext == "html" || ext == "htm") { + eth.openHtml(path) + }else if(ext == "qml"){ + addPlugin(path, {close: true, section: "apps"}) + } + } + + + function setWalletValue(value) { + walletValueLabel.text = value + } + + function loadPlugin(name) { + console.log("Loading plugin" + name) + var view = 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 + anchors.top: parent.top + anchors.topMargin: 40 + font.pointSize: 12 + text: "

Mist - Amalthea


Development

Jeffrey Wilcke
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: 300 + maximumWidth: 300 + maximumHeight: 50 + minimumHeight: 50 + title: "Connect to peer" + + ComboBox { + id: addrField + anchors.verticalCenter: parent.verticalCenter + anchors.left: parent.left + anchors.right: addPeerButton.left + anchors.leftMargin: 10 + anchors.rightMargin: 10 + onAccepted: { + eth.connectToPeer(addrField.currentText) + addPeerWin.visible = false + } + + editable: true + model: ListModel { id: pastPeers } + + Component.onCompleted: { + var ips = eth.pastPeers() + for(var i = 0; i < ips.length; i++) { + pastPeers.append({text: ips.get(i)}) + } + + pastPeers.insert(0, {text: "54.76.56.74:30303"}) + } + } + + Button { + id: addPeerButton + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + anchors.rightMargin: 10 + text: "Add" + onClicked: { + eth.connectToPeer(addrField.currentText) + addPeerWin.visible = false + } + } + Component.onCompleted: { + addrField.focus = true + } + } } diff --git a/javascript/types.go b/javascript/types.go index afa0a41c64e0..53a2977a820b 100644 --- a/javascript/types.go +++ b/javascript/types.go @@ -88,6 +88,10 @@ func (self *JSEthereum) GetStateObject(addr string) otto.Value { return self.toVal(&JSStateObject{ethpipe.NewJSObject(self.JSPipe.World().SafeGet(ethutil.Hex2Bytes(addr))), self}) } +func (self *JSEthereum) Peers() otto.Value { + return self.toVal(self.JSPipe.Peers()) +} + func (self *JSEthereum) Transact(key, recipient, valueStr, gasStr, gasPriceStr, dataStr string) otto.Value { r, err := self.JSPipe.Transact(key, recipient, valueStr, gasStr, gasPriceStr, dataStr) if err != nil { From e077cad3337b11300ddd56561f4ff7c5d09b015a Mon Sep 17 00:00:00 2001 From: obscuren Date: Thu, 18 Sep 2014 11:45:33 +0200 Subject: [PATCH 24/39] No default background color --- Mist/assets/icecream.png | Bin 0 -> 4643 bytes Mist/assets/qml/wallet.qml | 14 ++------------ 2 files changed, 2 insertions(+), 12 deletions(-) create mode 100644 Mist/assets/icecream.png diff --git a/Mist/assets/icecream.png b/Mist/assets/icecream.png new file mode 100644 index 0000000000000000000000000000000000000000..2438ca845b9fcf9228fd36333b305ed3da85f5ae GIT binary patch literal 4643 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 Date: Thu, 18 Sep 2014 22:08:51 +0100 Subject: [PATCH 25/39] Fix whitespace --- Mist/assets/qml/wallet.qml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Mist/assets/qml/wallet.qml b/Mist/assets/qml/wallet.qml index 0e3d321f468a..a7970d455bcf 100644 --- a/Mist/assets/qml/wallet.qml +++ b/Mist/assets/qml/wallet.qml @@ -101,16 +101,16 @@ ApplicationWindow { } } - menuBar: MenuBar { - Menu { - title: "File" - MenuItem { - text: "Import App" - shortcut: "Ctrl+o" - onTriggered: { - generalFileDialog.show(true, importApp) - } - } + menuBar: MenuBar { + Menu { + title: "File" + MenuItem { + text: "Import App" + shortcut: "Ctrl+o" + onTriggered: { + generalFileDialog.show(true, importApp) + } + } /* MenuItem { From d953415d9198e9d3da8145cda4ee90dae89c0f9f Mon Sep 17 00:00:00 2001 From: Joseph Goulden Date: Thu, 18 Sep 2014 22:26:48 +0100 Subject: [PATCH 26/39] Fix tabs --- Mist/assets/qml/wallet.qml | 1789 ++++++++++++++++++------------------ 1 file changed, 884 insertions(+), 905 deletions(-) diff --git a/Mist/assets/qml/wallet.qml b/Mist/assets/qml/wallet.qml index a7970d455bcf..9990da322a1c 100644 --- a/Mist/assets/qml/wallet.qml +++ b/Mist/assets/qml/wallet.qml @@ -10,909 +10,888 @@ import "../ext/filter.js" as Eth import "../ext/http.js" as Http ApplicationWindow { - id: root - - property alias miningButtonText: miningButton.text - property var ethx : Eth.ethx - - width: 900 - height: 600 - minimumHeight: 300 - - title: "Mist" - - // 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); - function invokeFilterCallback(data, receiverSeed) { - //var messages = JSON.parse(data) - // Signal handler - message(data, receiverSeed); - } - - TextField { - id: copyElementHax - visible: false - } - - function copyToClipboard(text) { - copyElementHax.text = text - copyElementHax.selectAll() - copyElementHax.copy() - } - - // Takes care of loading all default plugins - Component.onCompleted: { - addPlugin("./views/wallet.qml", {noAdd: true, section: "ethereum", active: true}); - addPlugin("./webapp.qml", {noAdd: true, section: "ethereum", active: true}); - - addPlugin("./views/transaction.qml", {noAdd: true, section: "legacy"}); - addPlugin("./views/chain.qml", {noAdd: true, section: "legacy"}); - addPlugin("./views/info.qml", {noAdd: true, section: "legacy"}); - addPlugin("./views/pending_tx.qml", {noAdd: true, section: "legacy"}); - addPlugin("./views/javascript.qml", {noAdd: true, section: "legacy"}); - - // Call the ready handler - gui.done(); - } - - function addViews(view, path, options) { - var views = mainSplit.addComponent(view, options) - views.menuItem.path = path - - mainSplit.views.push(views); - - if(!options.noAdd) { - gui.addPlugin(path) - } - - return views - } - - function addPlugin(path, options) { - try { - if(typeof(path) === "string" && /^https?/.test(path)) { - console.log('load http') - Http.request(path, function(o) { - if(o.status === 200) { - var view = Qt.createQmlObject(o.responseText, mainView, path) - addViews(view, path, options) - } - }) - - return - } - - var component = Qt.createComponent(path); - if(component.status != Component.Ready) { - if(component.status == Component.Error) { - ethx.note("error: ", component.errorString()); - } - - return - } - - var view = mainView.createView(component, options) - var views = addViews(view, path, options) - - return views.view - } catch(e) { - ethx.note(e) - } - } - - menuBar: MenuBar { - Menu { - title: "File" - MenuItem { - text: "Import App" - shortcut: "Ctrl+o" - onTriggered: { - generalFileDialog.show(true, importApp) - } - } - - /* - MenuItem { - text: "Browser" - onTriggered: eth.openBrowser() - } - */ - - MenuItem { - text: "Add plugin" - onTriggered: { - generalFileDialog.show(true, function(path) { - addPlugin(path, {canClose: true, section: "apps"}) - }) - } - } - - MenuSeparator {} - - MenuItem { - text: "Import key" - shortcut: "Ctrl+i" - onTriggered: { - generalFileDialog.show(true, function(path) { - gui.importKey(path) - }) - } - } - - MenuItem { - text: "Export keys" - shortcut: "Ctrl+e" - onTriggered: { - generalFileDialog.show(false, function(path) { - }) - } - } - - } - - Menu { - title: "Developer" - MenuItem { - text: "Debugger" - shortcut: "Ctrl+d" - onTriggered: eth.startDebugger() - } - - MenuItem { - text: "Import Tx" - onTriggered: { - txImportDialog.visible = true - } - } - - MenuItem { - text: "Run JS file" - onTriggered: { - generalFileDialog.show(true, function(path) { - eth.evalJavascriptFile(path) - }) - } - } - - MenuItem { - text: "Dump state" - onTriggered: { - generalFileDialog.show(false, function(path) { - // Empty hash for latest - gui.dumpState("", path) - }) - } - } - - MenuSeparator {} - - MenuItem { - id: miningSpeed - text: "Mining: Turbo" - onTriggered: { - gui.toggleTurboMining() - if(text == "Mining: Turbo") { - text = "Mining: Normal"; - } else { - text = "Mining: Turbo"; - } - } - } - } - - Menu { - title: "Network" - MenuItem { - text: "Add Peer" - shortcut: "Ctrl+p" - onTriggered: { - addPeerWin.visible = true - } - } - MenuItem { - text: "Show Peers" - shortcut: "Ctrl+e" - onTriggered: { - peerWindow.visible = true - } - } - } - - Menu { - title: "Help" - MenuItem { - text: "About" - onTriggered: { - aboutWin.visible = true - } - } - } - - Menu { - title: "GLOBAL SHORTCUTS" - visible: false - MenuItem { - visible: false - shortcut: "Ctrl+l" - onTriggered: { - url.focus = true - } - } - } - } - - statusBar: StatusBar { - height: 32 - RowLayout { - Button { - id: miningButton - text: "Start Mining" - onClicked: { - gui.toggleMining() - } - } - - /* - Button { - id: importAppButton - text: "Browser" - onClicked: { - eth.openBrowser() - } - } - */ - - RowLayout { - Label { - id: walletValueLabel - - font.pixelSize: 10 - styleColor: "#797979" - } - } - } - - Label { - y: 6 - objectName: "miningLabel" - visible: true - font.pixelSize: 10 - anchors.right: lastBlockLabel.left - anchors.rightMargin: 5 - } - - Label { - y: 6 - id: lastBlockLabel - objectName: "lastBlockLabel" - visible: true - text: "" - font.pixelSize: 10 - 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 - } - - Label { - id: peerLabel - font.pixelSize: 8 - text: "0 / 0" - } - Image { - id: peerImage - width: 10; height: 10 - source: "../network.png" - } - } - } - - - property var blockModel: ListModel { - id: blockModel - } - - SplitView { - property var views: []; - - id: mainSplit - anchors.fill: parent - resizing: false - - function setView(view, menu) { - for(var i = 0; i < views.length; i++) { - views[i].view.visible = false - views[i].menuItem.setSelection(false) - } - view.visible = true - - //menu.border.color = "#CCCCCC" - //menu.color = "#FFFFFFFF" - menu.setSelection(true) - } - - function addComponent(view, options) { - view.visible = false - view.anchors.fill = mainView - - if( !view.hasOwnProperty("iconSource") ) { - console.log("Could not load plugin. Property 'iconSourc' not found on view."); - return; - } - - var menuItem = menu.createMenuItem(view.iconSource, view, options); - if( view.hasOwnProperty("menuItem") ) { - view.menuItem = menuItem; - } - - if( view.hasOwnProperty("onReady") ) { - view.onReady.call(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" - - Component { - id: menuItemTemplate - Rectangle { - id: menuItem - property var view; - property var path; - - property alias title: label.text - property alias icon: icon.source - property alias secondaryTitle: secondary.text - function setSelection(on) { - sel.visible = on - } - - width: 176 - height: 28 - color: "#00000000" - - anchors { - left: parent.left - leftMargin: 4 - } - - Rectangle { - id: sel - visible: false - anchors.fill: parent - color: "#00000000" - Rectangle { - id: r - anchors.fill: parent - border.color: "#CCCCCC" - border.width: 1 - radius: 5 - color: "#FFFFFFFF" - } - Rectangle { - anchors { - top: r.top - bottom: r.bottom - right: r.right - } - width: 10 - color: "#FFFFFFFF" - - Rectangle { - anchors { - left: parent.left - right: parent.right - top: parent.top - } - height: 1 - color: "#CCCCCC" - } - - Rectangle { - anchors { - left: parent.left - right: parent.right - bottom: parent.bottom - } - height: 1 - color: "#CCCCCC" - } - } - } - - MouseArea { - anchors.fill: parent - onClicked: { - mainSplit.setView(view, menuItem) - } - } - - Image { - id: icon - height: 20 - width: 20 - anchors { - left: parent.left - verticalCenter: parent.verticalCenter - leftMargin: 3 - } - MouseArea { - anchors.fill: parent - onClicked: { - menuItem.closeApp() - } - } - } - - Text { - id: label - anchors { - left: icon.right - verticalCenter: parent.verticalCenter - leftMargin: 3 - } - - color: "#0D0A01" - font.pixelSize: 12 - } - - 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.iconSource - /* - 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: "APPS" - font.bold: true - anchors { - left: parent.left - leftMargin: 5 - } - color: "#888888" - } - - ColumnLayout { - id: menuApps - spacing: 3 - anchors { - left: parent.left - right: parent.right - } - } - - 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 { - anchors.right: parent.right - anchors.left: menu.right - anchors.bottom: parent.bottom - anchors.top: parent.top - color: "#00000000" - - Rectangle { - id: urlPane - height: 40 - color: "#00000000" - anchors { - left: parent.left - right: parent.right - leftMargin: 5 - rightMargin: 5 - top: parent.top - topMargin: 5 - } - TextField { - id: url - objectName: "url" - placeholderText: "DApp URL" - anchors { - left: parent.left - right: parent.right - top: parent.top - topMargin: 5 - rightMargin: 5 - leftMargin: 5 - } - - Keys.onReturnPressed: { - addPlugin(this.text, {canClose: true, section: "apps"}) - } - } - - } - - // Border - Rectangle { - id: divider - anchors { - left: parent.left - right: parent.right - top: urlPane.bottom - } - z: -1 - height: 1 - color: "#CCCCCC" - } - - Rectangle { - id: mainView - - - anchors.right: parent.right - anchors.left: parent.left - anchors.bottom: parent.bottom - anchors.top: divider.bottom - - function createView(component) { - var view = component.createObject(mainView) - - return view; - } - } - } - } - - - /****************** - * Dialogs - *****************/ - FileDialog { - id: generalFileDialog - property var callback; - onAccepted: { - var path = this.fileUrl.toString(); - callback.call(this, path); - } - - function show(selectExisting, callback) { - generalFileDialog.callback = callback; - generalFileDialog.selectExisting = selectExisting; - - this.open(); - } - } - - - /****************** - * 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 setWalletValue(value) { - walletValueLabel.text = value - } - - function loadPlugin(name) { - console.log("Loading plugin" + name) - var view = 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 - Aitne


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: 300 - maximumWidth: 300 - maximumHeight: 50 - minimumHeight: 50 - title: "Add peer" - - /* - TextField { - id: addrField - anchors.verticalCenter: parent.verticalCenter - anchors.left: parent.left - anchors.right: addPeerButton.left - anchors.leftMargin: 10 - anchors.rightMargin: 10 - placeholderText: "address:port" - text: "54.76.56.74:30303" - onAccepted: { - eth.connectToPeer(addrField.text) - addPeerWin.visible = false - } - } - */ - ComboBox { - id: addrField - anchors.verticalCenter: parent.verticalCenter - anchors.left: parent.left - anchors.right: addPeerButton.left - anchors.leftMargin: 10 - anchors.rightMargin: 10 - onAccepted: { - eth.connectToPeer(addrField.currentText) - addPeerWin.visible = false - } - - editable: true - model: ListModel { id: pastPeers } - - Component.onCompleted: { - var ips = eth.pastPeers() - for(var i = 0; i < ips.length; i++) { - pastPeers.append({text: ips.get(i)}) - } - - pastPeers.insert(0, {text: "54.76.56.74:30303"}) - } - } - - Button { - id: addPeerButton - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - anchors.rightMargin: 10 - text: "Add" - onClicked: { - eth.connectToPeer(addrField.currentText) - addPeerWin.visible = false - } - } - Component.onCompleted: { - addrField.focus = true - } - } + id: root + + property alias miningButtonText: miningButton.text + property var ethx : Eth.ethx + + width: 900 + height: 600 + minimumHeight: 300 + + title: "Mist" + + // 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); + function invokeFilterCallback(data, receiverSeed) { + //var messages = JSON.parse(data) + // Signal handler + message(data, receiverSeed); + } + + TextField { + id: copyElementHax + visible: false + } + + function copyToClipboard(text) { + copyElementHax.text = text + copyElementHax.selectAll() + copyElementHax.copy() + } + + // Takes care of loading all default plugins + Component.onCompleted: { + addPlugin("./views/wallet.qml", {noAdd: true, close: false, section: "ethereum", active: true}); + addPlugin("./webapp.qml", {noAdd: true, close: false, section: "ethereum", active: true}); + + addPlugin("./views/transaction.qml", {noAdd: true, close: false, section: "legacy"}); + addPlugin("./views/chain.qml", {noAdd: true, close: false, section: "legacy"}); + addPlugin("./views/info.qml", {noAdd: true, close: false, section: "legacy"}); + addPlugin("./views/pending_tx.qml", {noAdd: true, close: false, section: "legacy"}); + addPlugin("./views/javascript.qml", {noAdd: true, close: false, section: "legacy"}); + + // Call the ready handler + gui.done(); + } + + function addViews(view, path, options) { + var views = mainSplit.addComponent(view, options) + views.menuItem.path = path + + mainSplit.views.push(views); + + if(!options.noAdd) { + gui.addPlugin(path) + } + + return views + } + + function addPlugin(path, options) { + try { + if(typeof(path) === "string" && /^https?/.test(path)) { + console.log('load http') + Http.request(path, function(o) { + if(o.status === 200) { + var view = Qt.createQmlObject(o.responseText, mainView, path) + addViews(view, path, options) + } + }) + + return + } + + var component = Qt.createComponent(path); + if(component.status != Component.Ready) { + if(component.status == Component.Error) { + ethx.note("error: ", component.errorString()); + } + + return + } + + var view = mainView.createView(component, options) + var views = addViews(view, path, options) + + return views.view + } catch(e) { + ethx.note(e) + } + } + + menuBar: MenuBar { + Menu { + title: "File" + MenuItem { + text: "Import App" + shortcut: "Ctrl+o" + onTriggered: { + generalFileDialog.show(true, importApp) + } + } + + /* + MenuItem { + text: "Browser" + onTriggered: eth.openBrowser() + } + */ + + MenuItem { + text: "Add plugin" + onTriggered: { + generalFileDialog.show(true, function(path) { + addPlugin(path, {close: true, section: "apps"}) + }) + } + } + + MenuSeparator {} + + MenuItem { + text: "Import key" + shortcut: "Ctrl+i" + onTriggered: { + generalFileDialog.show(true, function(path) { + gui.importKey(path) + }) + } + } + + MenuItem { + text: "Export keys" + shortcut: "Ctrl+e" + onTriggered: { + generalFileDialog.show(false, function(path) { + }) + } + } + + } + + Menu { + title: "Developer" + MenuItem { + text: "Debugger" + shortcut: "Ctrl+d" + onTriggered: eth.startDebugger() + } + + MenuItem { + text: "Import Tx" + onTriggered: { + txImportDialog.visible = true + } + } + + MenuItem { + text: "Run JS file" + onTriggered: { + generalFileDialog.show(true, function(path) { + eth.evalJavascriptFile(path) + }) + } + } + + MenuItem { + text: "Dump state" + onTriggered: { + generalFileDialog.show(false, function(path) { + // Empty hash for latest + gui.dumpState("", path) + }) + } + } + + MenuSeparator {} + + MenuItem { + id: miningSpeed + text: "Mining: Turbo" + onTriggered: { + gui.toggleTurboMining() + if(text == "Mining: Turbo") { + text = "Mining: Normal"; + } else { + text = "Mining: Turbo"; + } + } + } + } + + Menu { + title: "Network" + MenuItem { + text: "Add Peer" + shortcut: "Ctrl+p" + onTriggered: { + addPeerWin.visible = true + } + } + MenuItem { + text: "Show Peers" + shortcut: "Ctrl+e" + onTriggered: { + peerWindow.visible = true + } + } + } + + Menu { + title: "Help" + MenuItem { + text: "About" + onTriggered: { + aboutWin.visible = true + } + } + } + + Menu { + title: "GLOBAL SHORTCUTS" + visible: false + MenuItem { + visible: false + shortcut: "Ctrl+l" + onTriggered: { + url.focus = true + } + } + } + } + + statusBar: StatusBar { + height: 32 + RowLayout { + Button { + id: miningButton + text: "Start Mining" + onClicked: { + gui.toggleMining() + } + } + + /* + Button { + id: importAppButton + text: "Browser" + onClicked: { + eth.openBrowser() + } + } + */ + + RowLayout { + Label { + id: walletValueLabel + + font.pixelSize: 10 + styleColor: "#797979" + } + } + } + + Label { + y: 6 + objectName: "miningLabel" + visible: true + font.pixelSize: 10 + anchors.right: lastBlockLabel.left + anchors.rightMargin: 5 + } + + Label { + y: 6 + id: lastBlockLabel + objectName: "lastBlockLabel" + visible: true + text: "" + font.pixelSize: 10 + 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 + } + + Label { + id: peerLabel + font.pixelSize: 8 + text: "0 / 0" + } + Image { + id: peerImage + width: 10; height: 10 + source: "../network.png" + } + } + } + + + property var blockModel: ListModel { + id: blockModel + } + + SplitView { + property var views: []; + + id: mainSplit + anchors.fill: parent + resizing: false + + function setView(view, menu) { + for(var i = 0; i < views.length; i++) { + views[i].view.visible = false + views[i].menuItem.setSelection(false) + } + view.visible = true + + //menu.border.color = "#CCCCCC" + //menu.color = "#FFFFFFFF" + menu.setSelection(true) + } + + function addComponent(view, options) { + view.visible = false + view.anchors.fill = mainView + + if( !view.hasOwnProperty("iconSource") ) { + console.log("Could not load plugin. Property 'iconSourc' not found on view."); + return; + } + + var menuItem = menu.createMenuItem(view.iconSource, view, options); + if( view.hasOwnProperty("menuItem") ) { + view.menuItem = menuItem; + } + + if( view.hasOwnProperty("onReady") ) { + view.onReady.call(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" + + Component { + id: menuItemTemplate + Rectangle { + id: menuItem + property var view; + property var path; + property var closable; + + property alias title: label.text + property alias icon: icon.source + property alias secondaryTitle: secondary.text + function setSelection(on) { + sel.visible = on + } + + width: 176 + height: 28 + color: "#00000000" + + anchors { + left: parent.left + leftMargin: 4 + } + + Rectangle { + id: sel + visible: false + anchors.fill: parent + color: "#00000000" + Rectangle { + id: r + anchors.fill: parent + border.color: "#CCCCCC" + border.width: 1 + radius: 5 + color: "#FFFFFFFF" + } + Rectangle { + anchors { + top: r.top + bottom: r.bottom + right: r.right + } + width: 10 + color: "#FFFFFFFF" + + Rectangle { + anchors { + left: parent.left + right: parent.right + top: parent.top + } + height: 1 + color: "#CCCCCC" + } + + Rectangle { + anchors { + left: parent.left + right: parent.right + bottom: parent.bottom + } + height: 1 + color: "#CCCCCC" + } + } + } + + MouseArea { + anchors.fill: parent + onClicked: { + mainSplit.setView(view, menuItem) + } + } + + Image { + id: icon + height: 20 + width: 20 + anchors { + left: parent.left + verticalCenter: parent.verticalCenter + leftMargin: 3 + } + MouseArea { + anchors.fill: parent + onClicked: { + menuItem.closeApp() + } + } + } + + Text { + id: label + anchors { + left: icon.right + verticalCenter: parent.verticalCenter + leftMargin: 3 + } + + color: "#0D0A01" + font.pixelSize: 12 + } + + Text { + id: secondary + anchors { + right: parent.right + rightMargin: 8 + verticalCenter: parent.verticalCenter + } + color: "#AEADBE" + font.pixelSize: 12 + } + + + function closeApp() { + if(!this.closable) { return; } + + 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.iconSource + comp.closable = options.close; + + return comp + } + + 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: "APPS" + font.bold: true + anchors { + left: parent.left + leftMargin: 5 + } + color: "#888888" + } + + ColumnLayout { + id: menuApps + spacing: 3 + anchors { + left: parent.left + right: parent.right + } + } + + 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 { + anchors.right: parent.right + anchors.left: menu.right + anchors.bottom: parent.bottom + anchors.top: parent.top + color: "#00000000" + + Rectangle { + id: urlPane + height: 40 + color: "#00000000" + anchors { + left: parent.left + right: parent.right + leftMargin: 5 + rightMargin: 5 + top: parent.top + topMargin: 5 + } + TextField { + id: url + objectName: "url" + placeholderText: "DApp URL" + anchors { + left: parent.left + right: parent.right + top: parent.top + topMargin: 5 + rightMargin: 5 + leftMargin: 5 + } + + Keys.onReturnPressed: { + addPlugin(this.text, {close: true, section: "apps"}) + } + } + + } + + // Border + Rectangle { + id: divider + anchors { + left: parent.left + right: parent.right + top: urlPane.bottom + } + z: -1 + height: 1 + color: "#CCCCCC" + } + + Rectangle { + id: mainView + + + anchors.right: parent.right + anchors.left: parent.left + anchors.bottom: parent.bottom + anchors.top: divider.bottom + + function createView(component) { + var view = component.createObject(mainView) + + return view; + } + } + } + } + + + /****************** + * Dialogs + *****************/ + FileDialog { + id: generalFileDialog + property var callback; + onAccepted: { + var path = this.fileUrl.toString(); + callback.call(this, path); + } + + function show(selectExisting, callback) { + generalFileDialog.callback = callback; + generalFileDialog.selectExisting = selectExisting; + + this.open(); + } + } + + + /****************** + * Wallet functions + *****************/ + function importApp(path) { + var ext = path.split('.').pop() + if(ext == "html" || ext == "htm") { + eth.openHtml(path) + }else if(ext == "qml"){ + addPlugin(path, {close: true, section: "apps"}) + } + } + + + function setWalletValue(value) { + walletValueLabel.text = value + } + + function loadPlugin(name) { + console.log("Loading plugin" + name) + var view = 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 + anchors.top: parent.top + anchors.topMargin: 40 + font.pointSize: 12 + text: "

Mist - Amalthea


Development

Jeffrey Wilcke
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: 300 + maximumWidth: 300 + maximumHeight: 50 + minimumHeight: 50 + title: "Connect to peer" + + ComboBox { + id: addrField + anchors.verticalCenter: parent.verticalCenter + anchors.left: parent.left + anchors.right: addPeerButton.left + anchors.leftMargin: 10 + anchors.rightMargin: 10 + onAccepted: { + eth.connectToPeer(addrField.currentText) + addPeerWin.visible = false + } + + editable: true + model: ListModel { id: pastPeers } + + Component.onCompleted: { + var ips = eth.pastPeers() + for(var i = 0; i < ips.length; i++) { + pastPeers.append({text: ips.get(i)}) + } + + pastPeers.insert(0, {text: "54.76.56.74:30303"}) + } + } + + Button { + id: addPeerButton + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + anchors.rightMargin: 10 + text: "Add" + onClicked: { + eth.connectToPeer(addrField.currentText) + addPeerWin.visible = false + } + } + Component.onCompleted: { + addrField.focus = true + } + } } From 02ea68f1f3b56c669b1a178c082d55ff27fd5826 Mon Sep 17 00:00:00 2001 From: obscuren Date: Fri, 19 Sep 2014 01:42:26 +0200 Subject: [PATCH 27/39] info --- Mist/assets/debugger/debugger.qml | 33 ++++++++++++++++++++----------- Mist/debugger.go | 2 ++ 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/Mist/assets/debugger/debugger.qml b/Mist/assets/debugger/debugger.qml index 34fe01253d77..902e09d2cfaf 100644 --- a/Mist/assets/debugger/debugger.qml +++ b/Mist/assets/debugger/debugger.qml @@ -223,18 +223,29 @@ ApplicationWindow { } } - Rectangle { - height: 200 - width: parent.width - TableView { - id: logTableView - property var logModel: ListModel { - id: logModel + SplitView { + Rectangle { + height: 200 + width: parent.width * 0.66 + TableView { + id: logTableView + property var logModel: ListModel { + id: logModel + } + height: parent.height + width: parent.width + TableViewColumn{ id: message ; role: "message" ; title: "log" ; width: logTableView.width - 2 } + model: logModel } - height: parent.height - width: parent.width - TableViewColumn{ id: message ; role: "message" ; title: "log" ; width: logTableView.width - 2 } - model: logModel + } + + TextArea { + objectName: "info" + anchors { + top: parent.top + bottom: parent.bottom + } + readOnly: true } } } diff --git a/Mist/debugger.go b/Mist/debugger.go index 7bc544377d70..2b90814191e9 100644 --- a/Mist/debugger.go +++ b/Mist/debugger.go @@ -284,6 +284,8 @@ func (d *Debugger) halting(pc int, op ethvm.OpCode, mem *ethvm.Memory, stack *et d.win.Root().Call("setStorage", storeVal{fmt.Sprintf("% x", key), fmt.Sprintf("% x", node.Str())}) }) + d.win.Root().ObjectByName("info").Set("text", fmt.Sprintf(`stack frame %v`, new(big.Int).SetBytes(mem.Get(0, 32)))) + out: for { select { From 9689a2012b54a2407619e1562fe7137f6734e8e6 Mon Sep 17 00:00:00 2001 From: obscuren Date: Fri, 19 Sep 2014 01:43:51 +0200 Subject: [PATCH 28/39] mist --- utils/cmd.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/cmd.go b/utils/cmd.go index da3cac444f16..700542cae42e 100644 --- a/utils/cmd.go +++ b/utils/cmd.go @@ -189,7 +189,7 @@ func DefaultAssetPath() string { // assume a debug build and use the source directory as // asset directory. pwd, _ := os.Getwd() - if pwd == path.Join(os.Getenv("GOPATH"), "src", "github.com", "ethereum", "go-ethereum", "Mist") { + if pwd == path.Join(os.Getenv("GOPATH"), "src", "github.com", "ethereum", "go-ethereum", "mist") { assetPath = path.Join(pwd, "assets") } else { switch runtime.GOOS { From 0a82e3b75b85631b1b3877b9518e782b182baf8a Mon Sep 17 00:00:00 2001 From: obscuren Date: Fri, 19 Sep 2014 11:13:01 +0200 Subject: [PATCH 29/39] Stack info --- Mist/assets/debugger/debugger.qml | 95 +++++++++++++++++++++++-------- Mist/debugger.go | 24 ++++++-- Mist/flags.go | 2 +- javascript/javascript_runtime.go | 2 +- 4 files changed, 92 insertions(+), 31 deletions(-) diff --git a/Mist/assets/debugger/debugger.qml b/Mist/assets/debugger/debugger.qml index 902e09d2cfaf..d4b8db576d8f 100644 --- a/Mist/assets/debugger/debugger.qml +++ b/Mist/assets/debugger/debugger.qml @@ -86,8 +86,37 @@ ApplicationWindow { TableView { id: asmTableView width: 200 + headerVisible: false TableViewColumn{ role: "value" ; title: "" ; width: asmTableView.width - 2 } model: asmModel + /* + alternatingRowColors: false + itemDelegate: Item { + Rectangle { + anchors.fill: parent + color: "#DDD" + Text { + anchors { + left: parent.left + right: parent.right + leftMargin: 10 + verticalCenter: parent.verticalCenter + } + color: "#333" + elide: styleData.elideMode + text: styleData.value + font.pixelSize: 11 + MouseArea { + acceptedButtons: Qt.LeftButton + anchors.fill: parent + onClicked: { + mouse.accepted = true + } + } + } + } + } + */ } Rectangle { @@ -201,8 +230,8 @@ ApplicationWindow { } height: parent.height width: parent.width - stackTableView.width - TableViewColumn{ id:mnumColmn ; role: "num" ; title: "#" ; width: 50} - TableViewColumn{ role: "value" ; title: "Memory" ; width: 750} + TableViewColumn{ id:mnumColmn ; role: "num" ; title: "#" ; width: 50 } + TableViewColumn{ role: "value" ; title: "Memory" ; width: 750 } model: memModel } } @@ -223,31 +252,21 @@ ApplicationWindow { } } - SplitView { - Rectangle { - height: 200 - width: parent.width * 0.66 - TableView { - id: logTableView - property var logModel: ListModel { - id: logModel - } - height: parent.height - width: parent.width - TableViewColumn{ id: message ; role: "message" ; title: "log" ; width: logTableView.width - 2 } - model: logModel - } - } - - TextArea { - objectName: "info" - anchors { - top: parent.top - bottom: parent.bottom + Rectangle { + height: 200 + width: parent.width * 0.66 + TableView { + id: logTableView + property var logModel: ListModel { + id: logModel } - readOnly: true + height: parent.height + width: parent.width + TableViewColumn{ id: message ; role: "message" ; title: "log" ; width: logTableView.width - 2 } + model: logModel } } + } } } @@ -271,12 +290,37 @@ ApplicationWindow { exec() } } + + RowLayout { + anchors.left: dbgCommand.right + anchors.leftMargin: 10 + spacing: 5 + y: parent.height / 2 - this.height / 2 + + Text { + objectName: "stackFrame" + font.pixelSize: 10 + text: "stack ptr: 0" + } + + Text { + objectName: "stackSize" + font.pixelSize: 10 + text: "stack size: 0" + } + + Text { + objectName: "memSize" + font.pixelSize: 10 + text: "mem size: 0" + } + } } toolBar: ToolBar { height: 30 RowLayout { - spacing: 5 + spacing: 10 Button { property var enabled: true @@ -338,6 +382,7 @@ ApplicationWindow { function setInstruction(num) { asmTableView.selection.clear() asmTableView.selection.select(num) + asmTableView.positionViewAtRow(num, ListView.Center) } function setMem(mem) { diff --git a/Mist/debugger.go b/Mist/debugger.go index 2b90814191e9..a9086921dd54 100644 --- a/Mist/debugger.go +++ b/Mist/debugger.go @@ -5,6 +5,7 @@ import ( "math/big" "strconv" "strings" + "unicode" "github.com/ethereum/eth-go/ethchain" "github.com/ethereum/eth-go/ethstate" @@ -271,9 +272,20 @@ func (d *Debugger) halting(pc int, op ethvm.OpCode, mem *ethvm.Memory, stack *et d.win.Root().Call("clearStorage") addr := 0 - for i := 0; i+32 <= mem.Len(); i += 32 { - d.win.Root().Call("setMem", memAddr{fmt.Sprintf("%03d", addr), fmt.Sprintf("% x", mem.Data()[i:i+32])}) - addr++ + for i := 0; i+32 <= mem.Len(); i += 16 { + dat := mem.Data()[i : i+16] + var str string + + for _, d := range dat { + if unicode.IsGraphic(rune(d)) { + str += string(d) + } else { + str += "?" + } + } + + d.win.Root().Call("setMem", memAddr{fmt.Sprintf("%03d", addr), fmt.Sprintf("%s % x", str, dat)}) + addr += 16 } for _, val := range stack.Data() { @@ -284,7 +296,11 @@ func (d *Debugger) halting(pc int, op ethvm.OpCode, mem *ethvm.Memory, stack *et d.win.Root().Call("setStorage", storeVal{fmt.Sprintf("% x", key), fmt.Sprintf("% x", node.Str())}) }) - d.win.Root().ObjectByName("info").Set("text", fmt.Sprintf(`stack frame %v`, new(big.Int).SetBytes(mem.Get(0, 32)))) + stackFrameAt := new(big.Int).SetBytes(mem.Get(0, 32)) + psize := mem.Len() - int(new(big.Int).SetBytes(mem.Get(0, 32)).Uint64()) + d.win.Root().ObjectByName("stackFrame").Set("text", fmt.Sprintf(`stack ptr: %v`, stackFrameAt)) + d.win.Root().ObjectByName("stackSize").Set("text", fmt.Sprintf(`stack size: %d`, psize)) + d.win.Root().ObjectByName("memSize").Set("text", fmt.Sprintf(`mem size: %v`, mem.Len())) out: for { diff --git a/Mist/flags.go b/Mist/flags.go index 388280b8c748..d2e7d3fb08dc 100644 --- a/Mist/flags.go +++ b/Mist/flags.go @@ -44,7 +44,7 @@ func defaultAssetPath() string { // assume a debug build and use the source directory as // asset directory. pwd, _ := os.Getwd() - if pwd == path.Join(os.Getenv("GOPATH"), "src", "github.com", "ethereum", "go-ethereum", "Mist") { + if pwd == path.Join(os.Getenv("GOPATH"), "src", "github.com", "ethereum", "go-ethereum", "mist") { assetPath = path.Join(pwd, "assets") } else { switch runtime.GOOS { diff --git a/javascript/javascript_runtime.go b/javascript/javascript_runtime.go index 94301b859a24..ffc672a63a41 100644 --- a/javascript/javascript_runtime.go +++ b/javascript/javascript_runtime.go @@ -42,7 +42,7 @@ func (jsre *JSRE) LoadExtFile(path string) { } func (jsre *JSRE) LoadIntFile(file string) { - assetPath := path.Join(os.Getenv("GOPATH"), "src", "github.com", "ethereum", "go-ethereum", "Mist", "assets", "ext") + assetPath := path.Join(os.Getenv("GOPATH"), "src", "github.com", "ethereum", "go-ethereum", "mist", "assets", "ext") jsre.LoadExtFile(path.Join(assetPath, file)) } From 723074e71bbe1638a5cec9b996b1eed07a76fd72 Mon Sep 17 00:00:00 2001 From: obscuren Date: Fri, 19 Sep 2014 13:32:52 +0200 Subject: [PATCH 30/39] dump --- ethereum/flags.go | 1 + ethereum/main.go | 2 ++ 2 files changed, 3 insertions(+) diff --git a/ethereum/flags.go b/ethereum/flags.go index 5ed208411b17..c488e631431d 100644 --- a/ethereum/flags.go +++ b/ethereum/flags.go @@ -74,6 +74,7 @@ func Init() { flag.IntVar(&LogLevel, "loglevel", int(ethlog.InfoLevel), "loglevel: 0-5: silent,error,warn,info,debug,debug detail)") 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(&ShowGenesis, "genesis", false, "Dump the genesis block") flag.BoolVar(&Dump, "dump", false, "output the ethereum state in JSON format. Sub args [number, hash]") flag.StringVar(&DumpHash, "hash", "", "specify arg in hex") diff --git a/ethereum/main.go b/ethereum/main.go index ab6ce18b21c9..df9737c1fa0d 100644 --- a/ethereum/main.go +++ b/ethereum/main.go @@ -76,6 +76,8 @@ func main() { os.Exit(1) } + fmt.Printf("RLP: %x\nstate: %x\nhash: %x\n", ethutil.Rlp(block), block.GetRoot(), block.Hash()) + // Leave the Println. This needs clean output for piping fmt.Printf("%s\n", block.State().Dump()) From ae1de6593c31fbaa4429588cea2702dd5b01a722 Mon Sep 17 00:00:00 2001 From: obscuren Date: Fri, 19 Sep 2014 13:33:15 +0200 Subject: [PATCH 31/39] renamed --- {Mist => mist}/assets/back.png | Bin {Mist => mist}/assets/browser.png | Bin {Mist => mist}/assets/bug.png | Bin {Mist => mist}/assets/close.png | Bin {Mist => mist}/assets/debugger/debugger.qml | 0 {Mist => mist}/assets/ext/big.js | 0 {Mist => mist}/assets/ext/ethereum.js | 0 {Mist => mist}/assets/ext/filter.js | 0 {Mist => mist}/assets/ext/home.html | 0 {Mist => mist}/assets/ext/http.js | 0 {Mist => mist}/assets/ext/pre.js | 0 {Mist => mist}/assets/ext/string.js | 0 {Mist => mist}/assets/ext/test.html | 0 {Mist => mist}/assets/facet.png | Bin {Mist => mist}/assets/heart.png | Bin {Mist => mist}/assets/icecream.png | Bin {Mist => mist}/assets/muted/codemirror.css | 0 {Mist => mist}/assets/muted/debugger.html | 0 {Mist => mist}/assets/muted/eclipse.css | 0 {Mist => mist}/assets/muted/index.html | 0 {Mist => mist}/assets/muted/lib/codemirror.js | 0 {Mist => mist}/assets/muted/lib/go.js | 0 {Mist => mist}/assets/muted/lib/matchbrackets.js | 0 {Mist => mist}/assets/muted/muted.js | 0 {Mist => mist}/assets/net.png | Bin {Mist => mist}/assets/network.png | Bin {Mist => mist}/assets/new.png | Bin {Mist => mist}/assets/pick.png | Bin {Mist => mist}/assets/qml/QmlApp.qml | 0 {Mist => mist}/assets/qml/first_run.qml | 0 {Mist => mist}/assets/qml/muted.qml | 0 {Mist => mist}/assets/qml/test_app.qml | 0 {Mist => mist}/assets/qml/transactions.qml | 0 {Mist => mist}/assets/qml/views/chain.qml | 0 {Mist => mist}/assets/qml/views/history.qml | 0 {Mist => mist}/assets/qml/views/info.qml | 0 {Mist => mist}/assets/qml/views/javascript.qml | 0 {Mist => mist}/assets/qml/views/pending_tx.qml | 0 {Mist => mist}/assets/qml/views/transaction.qml | 0 {Mist => mist}/assets/qml/views/wallet.qml | 0 {Mist => mist}/assets/qml/wallet.qml | 0 {Mist => mist}/assets/qml/webapp.qml | 0 {Mist => mist}/assets/tx.png | Bin {Mist => mist}/assets/util/test.html | 0 {Mist => mist}/assets/wallet.png | Bin {Mist => mist}/bindings.go | 0 {Mist => mist}/debugger.go | 0 {Mist => mist}/errors.go | 0 {Mist => mist}/ext_app.go | 0 {Mist => mist}/flags.go | 0 {Mist => mist}/gui.go | 0 {Mist => mist}/html_container.go | 0 {Mist => mist}/main.go | 0 {Mist => mist}/qml_container.go | 0 {Mist => mist}/ui_lib.go | 0 55 files changed, 0 insertions(+), 0 deletions(-) rename {Mist => mist}/assets/back.png (100%) rename {Mist => mist}/assets/browser.png (100%) rename {Mist => mist}/assets/bug.png (100%) rename {Mist => mist}/assets/close.png (100%) rename {Mist => mist}/assets/debugger/debugger.qml (100%) rename {Mist => mist}/assets/ext/big.js (100%) rename {Mist => mist}/assets/ext/ethereum.js (100%) rename {Mist => mist}/assets/ext/filter.js (100%) rename {Mist => mist}/assets/ext/home.html (100%) rename {Mist => mist}/assets/ext/http.js (100%) rename {Mist => mist}/assets/ext/pre.js (100%) rename {Mist => mist}/assets/ext/string.js (100%) rename {Mist => mist}/assets/ext/test.html (100%) rename {Mist => mist}/assets/facet.png (100%) rename {Mist => mist}/assets/heart.png (100%) rename {Mist => mist}/assets/icecream.png (100%) rename {Mist => mist}/assets/muted/codemirror.css (100%) rename {Mist => mist}/assets/muted/debugger.html (100%) rename {Mist => mist}/assets/muted/eclipse.css (100%) rename {Mist => mist}/assets/muted/index.html (100%) rename {Mist => mist}/assets/muted/lib/codemirror.js (100%) rename {Mist => mist}/assets/muted/lib/go.js (100%) rename {Mist => mist}/assets/muted/lib/matchbrackets.js (100%) rename {Mist => mist}/assets/muted/muted.js (100%) rename {Mist => mist}/assets/net.png (100%) rename {Mist => mist}/assets/network.png (100%) rename {Mist => mist}/assets/new.png (100%) rename {Mist => mist}/assets/pick.png (100%) rename {Mist => mist}/assets/qml/QmlApp.qml (100%) rename {Mist => mist}/assets/qml/first_run.qml (100%) rename {Mist => mist}/assets/qml/muted.qml (100%) rename {Mist => mist}/assets/qml/test_app.qml (100%) rename {Mist => mist}/assets/qml/transactions.qml (100%) rename {Mist => mist}/assets/qml/views/chain.qml (100%) rename {Mist => mist}/assets/qml/views/history.qml (100%) rename {Mist => mist}/assets/qml/views/info.qml (100%) rename {Mist => mist}/assets/qml/views/javascript.qml (100%) rename {Mist => mist}/assets/qml/views/pending_tx.qml (100%) rename {Mist => mist}/assets/qml/views/transaction.qml (100%) rename {Mist => mist}/assets/qml/views/wallet.qml (100%) rename {Mist => mist}/assets/qml/wallet.qml (100%) rename {Mist => mist}/assets/qml/webapp.qml (100%) rename {Mist => mist}/assets/tx.png (100%) rename {Mist => mist}/assets/util/test.html (100%) rename {Mist => mist}/assets/wallet.png (100%) rename {Mist => mist}/bindings.go (100%) rename {Mist => mist}/debugger.go (100%) rename {Mist => mist}/errors.go (100%) rename {Mist => mist}/ext_app.go (100%) rename {Mist => mist}/flags.go (100%) rename {Mist => mist}/gui.go (100%) rename {Mist => mist}/html_container.go (100%) rename {Mist => mist}/main.go (100%) rename {Mist => mist}/qml_container.go (100%) rename {Mist => mist}/ui_lib.go (100%) diff --git a/Mist/assets/back.png b/mist/assets/back.png similarity index 100% rename from Mist/assets/back.png rename to mist/assets/back.png diff --git a/Mist/assets/browser.png b/mist/assets/browser.png similarity index 100% rename from Mist/assets/browser.png rename to mist/assets/browser.png diff --git a/Mist/assets/bug.png b/mist/assets/bug.png similarity index 100% rename from Mist/assets/bug.png rename to mist/assets/bug.png diff --git a/Mist/assets/close.png b/mist/assets/close.png similarity index 100% rename from Mist/assets/close.png rename to mist/assets/close.png diff --git a/Mist/assets/debugger/debugger.qml b/mist/assets/debugger/debugger.qml similarity index 100% rename from Mist/assets/debugger/debugger.qml rename to mist/assets/debugger/debugger.qml diff --git a/Mist/assets/ext/big.js b/mist/assets/ext/big.js similarity index 100% rename from Mist/assets/ext/big.js rename to mist/assets/ext/big.js diff --git a/Mist/assets/ext/ethereum.js b/mist/assets/ext/ethereum.js similarity index 100% rename from Mist/assets/ext/ethereum.js rename to mist/assets/ext/ethereum.js diff --git a/Mist/assets/ext/filter.js b/mist/assets/ext/filter.js similarity index 100% rename from Mist/assets/ext/filter.js rename to mist/assets/ext/filter.js diff --git a/Mist/assets/ext/home.html b/mist/assets/ext/home.html similarity index 100% rename from Mist/assets/ext/home.html rename to mist/assets/ext/home.html diff --git a/Mist/assets/ext/http.js b/mist/assets/ext/http.js similarity index 100% rename from Mist/assets/ext/http.js rename to mist/assets/ext/http.js diff --git a/Mist/assets/ext/pre.js b/mist/assets/ext/pre.js similarity index 100% rename from Mist/assets/ext/pre.js rename to mist/assets/ext/pre.js diff --git a/Mist/assets/ext/string.js b/mist/assets/ext/string.js similarity index 100% rename from Mist/assets/ext/string.js rename to mist/assets/ext/string.js diff --git a/Mist/assets/ext/test.html b/mist/assets/ext/test.html similarity index 100% rename from Mist/assets/ext/test.html rename to mist/assets/ext/test.html diff --git a/Mist/assets/facet.png b/mist/assets/facet.png similarity index 100% rename from Mist/assets/facet.png rename to mist/assets/facet.png diff --git a/Mist/assets/heart.png b/mist/assets/heart.png similarity index 100% rename from Mist/assets/heart.png rename to mist/assets/heart.png diff --git a/Mist/assets/icecream.png b/mist/assets/icecream.png similarity index 100% rename from Mist/assets/icecream.png rename to mist/assets/icecream.png diff --git a/Mist/assets/muted/codemirror.css b/mist/assets/muted/codemirror.css similarity index 100% rename from Mist/assets/muted/codemirror.css rename to mist/assets/muted/codemirror.css diff --git a/Mist/assets/muted/debugger.html b/mist/assets/muted/debugger.html similarity index 100% rename from Mist/assets/muted/debugger.html rename to mist/assets/muted/debugger.html diff --git a/Mist/assets/muted/eclipse.css b/mist/assets/muted/eclipse.css similarity index 100% rename from Mist/assets/muted/eclipse.css rename to mist/assets/muted/eclipse.css diff --git a/Mist/assets/muted/index.html b/mist/assets/muted/index.html similarity index 100% rename from Mist/assets/muted/index.html rename to mist/assets/muted/index.html diff --git a/Mist/assets/muted/lib/codemirror.js b/mist/assets/muted/lib/codemirror.js similarity index 100% rename from Mist/assets/muted/lib/codemirror.js rename to mist/assets/muted/lib/codemirror.js diff --git a/Mist/assets/muted/lib/go.js b/mist/assets/muted/lib/go.js similarity index 100% rename from Mist/assets/muted/lib/go.js rename to mist/assets/muted/lib/go.js diff --git a/Mist/assets/muted/lib/matchbrackets.js b/mist/assets/muted/lib/matchbrackets.js similarity index 100% rename from Mist/assets/muted/lib/matchbrackets.js rename to mist/assets/muted/lib/matchbrackets.js diff --git a/Mist/assets/muted/muted.js b/mist/assets/muted/muted.js similarity index 100% rename from Mist/assets/muted/muted.js rename to mist/assets/muted/muted.js diff --git a/Mist/assets/net.png b/mist/assets/net.png similarity index 100% rename from Mist/assets/net.png rename to mist/assets/net.png diff --git a/Mist/assets/network.png b/mist/assets/network.png similarity index 100% rename from Mist/assets/network.png rename to mist/assets/network.png diff --git a/Mist/assets/new.png b/mist/assets/new.png similarity index 100% rename from Mist/assets/new.png rename to mist/assets/new.png diff --git a/Mist/assets/pick.png b/mist/assets/pick.png similarity index 100% rename from Mist/assets/pick.png rename to mist/assets/pick.png diff --git a/Mist/assets/qml/QmlApp.qml b/mist/assets/qml/QmlApp.qml similarity index 100% rename from Mist/assets/qml/QmlApp.qml rename to mist/assets/qml/QmlApp.qml diff --git a/Mist/assets/qml/first_run.qml b/mist/assets/qml/first_run.qml similarity index 100% rename from Mist/assets/qml/first_run.qml rename to mist/assets/qml/first_run.qml diff --git a/Mist/assets/qml/muted.qml b/mist/assets/qml/muted.qml similarity index 100% rename from Mist/assets/qml/muted.qml rename to mist/assets/qml/muted.qml diff --git a/Mist/assets/qml/test_app.qml b/mist/assets/qml/test_app.qml similarity index 100% rename from Mist/assets/qml/test_app.qml rename to mist/assets/qml/test_app.qml diff --git a/Mist/assets/qml/transactions.qml b/mist/assets/qml/transactions.qml similarity index 100% rename from Mist/assets/qml/transactions.qml rename to mist/assets/qml/transactions.qml diff --git a/Mist/assets/qml/views/chain.qml b/mist/assets/qml/views/chain.qml similarity index 100% rename from Mist/assets/qml/views/chain.qml rename to mist/assets/qml/views/chain.qml diff --git a/Mist/assets/qml/views/history.qml b/mist/assets/qml/views/history.qml similarity index 100% rename from Mist/assets/qml/views/history.qml rename to mist/assets/qml/views/history.qml diff --git a/Mist/assets/qml/views/info.qml b/mist/assets/qml/views/info.qml similarity index 100% rename from Mist/assets/qml/views/info.qml rename to mist/assets/qml/views/info.qml diff --git a/Mist/assets/qml/views/javascript.qml b/mist/assets/qml/views/javascript.qml similarity index 100% rename from Mist/assets/qml/views/javascript.qml rename to mist/assets/qml/views/javascript.qml diff --git a/Mist/assets/qml/views/pending_tx.qml b/mist/assets/qml/views/pending_tx.qml similarity index 100% rename from Mist/assets/qml/views/pending_tx.qml rename to mist/assets/qml/views/pending_tx.qml diff --git a/Mist/assets/qml/views/transaction.qml b/mist/assets/qml/views/transaction.qml similarity index 100% rename from Mist/assets/qml/views/transaction.qml rename to mist/assets/qml/views/transaction.qml diff --git a/Mist/assets/qml/views/wallet.qml b/mist/assets/qml/views/wallet.qml similarity index 100% rename from Mist/assets/qml/views/wallet.qml rename to mist/assets/qml/views/wallet.qml diff --git a/Mist/assets/qml/wallet.qml b/mist/assets/qml/wallet.qml similarity index 100% rename from Mist/assets/qml/wallet.qml rename to mist/assets/qml/wallet.qml diff --git a/Mist/assets/qml/webapp.qml b/mist/assets/qml/webapp.qml similarity index 100% rename from Mist/assets/qml/webapp.qml rename to mist/assets/qml/webapp.qml diff --git a/Mist/assets/tx.png b/mist/assets/tx.png similarity index 100% rename from Mist/assets/tx.png rename to mist/assets/tx.png diff --git a/Mist/assets/util/test.html b/mist/assets/util/test.html similarity index 100% rename from Mist/assets/util/test.html rename to mist/assets/util/test.html diff --git a/Mist/assets/wallet.png b/mist/assets/wallet.png similarity index 100% rename from Mist/assets/wallet.png rename to mist/assets/wallet.png diff --git a/Mist/bindings.go b/mist/bindings.go similarity index 100% rename from Mist/bindings.go rename to mist/bindings.go diff --git a/Mist/debugger.go b/mist/debugger.go similarity index 100% rename from Mist/debugger.go rename to mist/debugger.go diff --git a/Mist/errors.go b/mist/errors.go similarity index 100% rename from Mist/errors.go rename to mist/errors.go diff --git a/Mist/ext_app.go b/mist/ext_app.go similarity index 100% rename from Mist/ext_app.go rename to mist/ext_app.go diff --git a/Mist/flags.go b/mist/flags.go similarity index 100% rename from Mist/flags.go rename to mist/flags.go diff --git a/Mist/gui.go b/mist/gui.go similarity index 100% rename from Mist/gui.go rename to mist/gui.go diff --git a/Mist/html_container.go b/mist/html_container.go similarity index 100% rename from Mist/html_container.go rename to mist/html_container.go diff --git a/Mist/main.go b/mist/main.go similarity index 100% rename from Mist/main.go rename to mist/main.go diff --git a/Mist/qml_container.go b/mist/qml_container.go similarity index 100% rename from Mist/qml_container.go rename to mist/qml_container.go diff --git a/Mist/ui_lib.go b/mist/ui_lib.go similarity index 100% rename from Mist/ui_lib.go rename to mist/ui_lib.go From 8585e59718e6b75a38833801f1725730c1b9fb01 Mon Sep 17 00:00:00 2001 From: obscuren Date: Fri, 19 Sep 2014 22:42:55 +0200 Subject: [PATCH 32/39] Re-writing ethereum.js. Added future/promises support. --- mist/assets/debugger/debugger.qml | 848 ++++++------- mist/assets/ext/html_messaging.js | 149 +++ mist/assets/ext/pre.js | 4 + mist/assets/ext/q.js | 1909 +++++++++++++++++++++++++++++ mist/assets/ext/qml_messaging.js | 13 + mist/assets/qml/webapp.qml | 29 +- mist/debugger.go | 10 +- mist/ui_lib.go | 9 + 8 files changed, 2537 insertions(+), 434 deletions(-) create mode 100644 mist/assets/ext/html_messaging.js create mode 100644 mist/assets/ext/q.js create mode 100644 mist/assets/ext/qml_messaging.js diff --git a/mist/assets/debugger/debugger.qml b/mist/assets/debugger/debugger.qml index d4b8db576d8f..2309a443bf3b 100644 --- a/mist/assets/debugger/debugger.qml +++ b/mist/assets/debugger/debugger.qml @@ -7,427 +7,429 @@ import QtQuick.Controls.Styles 1.1 import Ethereum 1.0 ApplicationWindow { - id: win - visible: false - title: "IceCREAM" - minimumWidth: 1280 - minimumHeight: 700 - width: 1290 - height: 750 - - property alias codeText: codeEditor.text - property alias dataText: rawDataField.text - - onClosing: { - //compileTimer.stop() - } - - MenuBar { - Menu { - title: "Debugger" - MenuItem { - text: "Run" - shortcut: "Ctrl+r" - onTriggered: debugCurrent() - } - - MenuItem { - text: "Next" - shortcut: "Ctrl+n" - onTriggered: dbg.next() - } - - MenuItem { - text: "Continue" - shortcut: "Ctrl+g" - onTriggered: dbg.continue() - } - MenuItem { - text: "Command" - shortcut: "Ctrl+l" - onTriggered: { - dbgCommand.focus = true - } - } - MenuItem { - text: "Focus code" - shortcut: "Ctrl+1" - onTriggered: { - codeEditor.focus = true - } - } - MenuItem { - text: "Focus data" - shortcut: "Ctrl+2" - onTriggered: { - rawDataField.focus = true - } - } - - /* - MenuItem { - text: "Close window" - shortcut: "Ctrl+w" - onTriggered: { - win.close() - } - } - */ - } - } - - - SplitView { - anchors.fill: parent - property var asmModel: ListModel { - id: asmModel - } - - TableView { - id: asmTableView - width: 200 - headerVisible: false - TableViewColumn{ role: "value" ; title: "" ; width: asmTableView.width - 2 } - model: asmModel - /* - alternatingRowColors: false - itemDelegate: Item { - Rectangle { - anchors.fill: parent - color: "#DDD" - Text { - anchors { - left: parent.left - right: parent.right - leftMargin: 10 - verticalCenter: parent.verticalCenter - } - color: "#333" - elide: styleData.elideMode - text: styleData.value - font.pixelSize: 11 - MouseArea { - acceptedButtons: Qt.LeftButton - anchors.fill: parent - onClicked: { - mouse.accepted = true - } - } - } - } - } - */ - } - - Rectangle { - color: "#00000000" - anchors.left: asmTableView.right - anchors.right: parent.right - SplitView { - orientation: Qt.Vertical - anchors.fill: parent - - Rectangle { - color: "#00000000" - height: 330 - anchors.left: parent.left - anchors.right: parent.right - - TextArea { - id: codeEditor - anchors.top: parent.top - anchors.bottom: parent.bottom - anchors.left: parent.left - anchors.right: settings.left - focus: true - - /* - Timer { - id: compileTimer - interval: 500 ; running: true ; repeat: true - onTriggered: { - dbg.autoComp(codeEditor.text) - } - } - */ - } - - Column { - id: settings - spacing: 5 - width: 300 - height: parent.height - anchors.right: parent.right - anchors.top: parent.top - anchors.bottom: parent.bottom - - Label { - text: "Arbitrary data" - } - TextArea { - id: rawDataField - anchors.left: parent.left - anchors.right: parent.right - height: 150 - } - - Label { - text: "Amount" - } - TextField { - id: txValue - width: 200 - placeholderText: "Amount" - validator: RegExpValidator { regExp: /\d*/ } - } - Label { - text: "Amount of gas" - } - TextField { - id: txGas - width: 200 - validator: RegExpValidator { regExp: /\d*/ } - text: "10000" - placeholderText: "Gas" - } - Label { - text: "Gas price" - } - TextField { - id: txGasPrice - width: 200 - placeholderText: "Gas price" - text: "1000000000000" - validator: RegExpValidator { regExp: /\d*/ } - } - } - } - - SplitView { - orientation: Qt.Vertical - id: inspectorPane - height: 500 - - SplitView { - orientation: Qt.Horizontal - height: 150 - - TableView { - id: stackTableView - property var stackModel: ListModel { - id: stackModel - } - height: parent.height - width: 300 - TableViewColumn{ role: "value" ; title: "Temp" ; width: 200 } - model: stackModel - } - - TableView { - id: memoryTableView - property var memModel: ListModel { - id: memModel - } - height: parent.height - width: parent.width - stackTableView.width - TableViewColumn{ id:mnumColmn ; role: "num" ; title: "#" ; width: 50 } - TableViewColumn{ role: "value" ; title: "Memory" ; width: 750 } - model: memModel - } - } - - Rectangle { - height: 100 - width: parent.width - TableView { - id: storageTableView - property var memModel: ListModel { - id: storageModel - } - height: parent.height - width: parent.width - TableViewColumn{ id: key ; role: "key" ; title: "#" ; width: storageTableView.width / 2} - TableViewColumn{ role: "value" ; title: "Storage" ; width: storageTableView.width / 2} - model: storageModel - } - } - - Rectangle { - height: 200 - width: parent.width * 0.66 - TableView { - id: logTableView - property var logModel: ListModel { - id: logModel - } - height: parent.height - width: parent.width - TableViewColumn{ id: message ; role: "message" ; title: "log" ; width: logTableView.width - 2 } - model: logModel - } - } - - } - } - } - } - - function exec() { - dbg.execCommand(dbgCommand.text); - dbgCommand.text = ""; - } - statusBar: StatusBar { - height: 30 - - - TextField { - id: dbgCommand - y: 1 - x: asmTableView.width - width: 500 - placeholderText: "Debugger (type 'help')" - Keys.onReturnPressed: { - exec() - } - } - - RowLayout { - anchors.left: dbgCommand.right - anchors.leftMargin: 10 - spacing: 5 - y: parent.height / 2 - this.height / 2 - - Text { - objectName: "stackFrame" - font.pixelSize: 10 - text: "stack ptr: 0" - } - - Text { - objectName: "stackSize" - font.pixelSize: 10 - text: "stack size: 0" - } - - Text { - objectName: "memSize" - font.pixelSize: 10 - text: "mem size: 0" - } - } - } - - toolBar: ToolBar { - height: 30 - RowLayout { - spacing: 10 - - Button { - property var enabled: true - id: debugStart - onClicked: { - debugCurrent() - } - text: "Debug" - } - - Button { - property var enabled: true - id: debugNextButton - onClicked: { - dbg.next() - } - text: "Next" - } - - Button { - id: debugContinueButton - onClicked: { - dbg.continue() - } - text: "Continue" - } - } - - - ComboBox { - id: snippets - anchors.right: parent.right - model: ListModel { - ListElement { text: "Snippets" ; value: "" } - ListElement { text: "Call Contract" ; value: "var[2] in;\nvar ret;\n\nin[0] = \"arg1\"\nin[1] = 0xdeadbeef\n\nvar success = call(0x0c542ddea93dae0c2fcb2cf175f03ad80d6be9a0, 0, 7000, in, ret)\n\nreturn ret" } - } - onCurrentIndexChanged: { - if(currentIndex != 0) { - var code = snippets.model.get(currentIndex).value; - codeEditor.insert(codeEditor.cursorPosition, code) - } - } - } - - } - - function debugCurrent() { - dbg.debug(txValue.text, txGas.text, txGasPrice.text, codeEditor.text, rawDataField.text) - } - - function setAsm(asm) { - asmModel.append({asm: asm}) - } - - function clearAsm() { - asmModel.clear() - } - - function setInstruction(num) { - asmTableView.selection.clear() - asmTableView.selection.select(num) - asmTableView.positionViewAtRow(num, ListView.Center) - } - - function setMem(mem) { - memModel.append({num: mem.num, value: mem.value}) - } - function clearMem(){ - memModel.clear() - } - - function setStack(stack) { - stackModel.append({value: stack}) - } - function addDebugMessage(message){ - debuggerLog.append({value: message}) - } - - function clearStack() { - stackModel.clear() - } - - function clearStorage() { - storageModel.clear() - } - - function setStorage(storage) { - storageModel.append({key: storage.key, value: storage.value}) - } - - function setLog(msg) { - // Remove first item once we've reached max log items - if(logModel.count > 250) { - logModel.remove(0) - } - - if(msg.len != 0) { - if(logTableView.flickableItem.atYEnd) { - logModel.append({message: msg}) - logTableView.positionViewAtRow(logTableView.rowCount - 1, ListView.Contain) - } else { - logModel.append({message: msg}) - } - } - } - - function clearLog() { - logModel.clear() - } + id: win + visible: false + title: "IceCREAM" + minimumWidth: 1280 + minimumHeight: 700 + width: 1290 + height: 750 + + property alias codeText: codeEditor.text + property alias dataText: rawDataField.text + + onClosing: { + dbg.Stop() + } + + menuBar: MenuBar { + Menu { + title: "Edit" + MenuItem { + text: "Focus code" + shortcut: "Ctrl+1" + onTriggered: { + codeEditor.focus = true + } + } + MenuItem { + text: "Focus data" + shortcut: "Ctrl+2" + onTriggered: { + rawDataField.focus = true + } + } + + MenuItem { + text: "Command" + shortcut: "Ctrl+l" + onTriggered: { + dbgCommand.focus = true + } + } + } + + Menu { + title: "Debugger" + MenuItem { + text: "Run" + shortcut: "Ctrl+r" + onTriggered: debugCurrent() + } + + MenuItem { + text: "Stop" + onTriggered: dbp.stop() + } + + MenuSeparator {} + + MenuItem { + text: "Next" + shortcut: "Ctrl+n" + onTriggered: dbg.next() + } + + MenuItem { + text: "Continue" + shortcut: "Ctrl+g" + onTriggered: dbg.continue() + } + } + } + + + SplitView { + anchors.fill: parent + property var asmModel: ListModel { + id: asmModel + } + + TableView { + id: asmTableView + width: 200 + headerVisible: false + TableViewColumn{ role: "value" ; title: "" ; width: asmTableView.width - 2 } + model: asmModel + /* + alternatingRowColors: false + itemDelegate: Item { + Rectangle { + anchors.fill: parent + color: "#DDD" + Text { + anchors { + left: parent.left + right: parent.right + leftMargin: 10 + verticalCenter: parent.verticalCenter + } + color: "#333" + elide: styleData.elideMode + text: styleData.value + font.pixelSize: 11 + MouseArea { + acceptedButtons: Qt.LeftButton + anchors.fill: parent + onClicked: { + mouse.accepted = true + } + } + } + } + } + */ + } + + Rectangle { + color: "#00000000" + anchors.left: asmTableView.right + anchors.right: parent.right + SplitView { + orientation: Qt.Vertical + anchors.fill: parent + + Rectangle { + color: "#00000000" + height: 330 + anchors.left: parent.left + anchors.right: parent.right + + TextArea { + id: codeEditor + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.left: parent.left + anchors.right: settings.left + focus: true + + /* + Timer { + id: compileTimer + interval: 500 ; running: true ; repeat: true + onTriggered: { + dbg.autoComp(codeEditor.text) + } + } + */ + } + + Column { + id: settings + spacing: 5 + width: 300 + height: parent.height + anchors.right: parent.right + anchors.top: parent.top + anchors.bottom: parent.bottom + + Label { + text: "Arbitrary data" + } + TextArea { + id: rawDataField + anchors.left: parent.left + anchors.right: parent.right + height: 150 + } + + Label { + text: "Amount" + } + TextField { + id: txValue + width: 200 + placeholderText: "Amount" + validator: RegExpValidator { regExp: /\d*/ } + } + Label { + text: "Amount of gas" + } + TextField { + id: txGas + width: 200 + validator: RegExpValidator { regExp: /\d*/ } + text: "10000" + placeholderText: "Gas" + } + Label { + text: "Gas price" + } + TextField { + id: txGasPrice + width: 200 + placeholderText: "Gas price" + text: "1000000000000" + validator: RegExpValidator { regExp: /\d*/ } + } + } + } + + SplitView { + orientation: Qt.Vertical + id: inspectorPane + height: 500 + + SplitView { + orientation: Qt.Horizontal + height: 150 + + TableView { + id: stackTableView + property var stackModel: ListModel { + id: stackModel + } + height: parent.height + width: 300 + TableViewColumn{ role: "value" ; title: "Temp" ; width: 200 } + model: stackModel + } + + TableView { + id: memoryTableView + property var memModel: ListModel { + id: memModel + } + height: parent.height + width: parent.width - stackTableView.width + TableViewColumn{ id:mnumColmn ; role: "num" ; title: "#" ; width: 50 } + TableViewColumn{ role: "value" ; title: "Memory" ; width: 750 } + model: memModel + } + } + + Rectangle { + height: 100 + width: parent.width + TableView { + id: storageTableView + property var memModel: ListModel { + id: storageModel + } + height: parent.height + width: parent.width + TableViewColumn{ id: key ; role: "key" ; title: "#" ; width: storageTableView.width / 2} + TableViewColumn{ role: "value" ; title: "Storage" ; width: storageTableView.width / 2} + model: storageModel + } + } + + Rectangle { + height: 200 + width: parent.width * 0.66 + TableView { + id: logTableView + property var logModel: ListModel { + id: logModel + } + height: parent.height + width: parent.width + TableViewColumn{ id: message ; role: "message" ; title: "log" ; width: logTableView.width - 2 } + model: logModel + } + } + + } + } + } + } + + function exec() { + dbg.execCommand(dbgCommand.text); + dbgCommand.text = ""; + } + statusBar: StatusBar { + height: 30 + + + TextField { + id: dbgCommand + y: 1 + x: asmTableView.width + width: 500 + placeholderText: "Debugger (type 'help')" + Keys.onReturnPressed: { + exec() + } + } + + RowLayout { + anchors.left: dbgCommand.right + anchors.leftMargin: 10 + spacing: 5 + y: parent.height / 2 - this.height / 2 + + Text { + objectName: "stackFrame" + font.pixelSize: 10 + text: "stack ptr: 0" + } + + Text { + objectName: "stackSize" + font.pixelSize: 10 + text: "stack size: 0" + } + + Text { + objectName: "memSize" + font.pixelSize: 10 + text: "mem size: 0" + } + } + } + + toolBar: ToolBar { + height: 30 + RowLayout { + spacing: 10 + + Button { + property var enabled: true + id: debugStart + onClicked: { + debugCurrent() + } + text: "Debug" + } + + Button { + property var enabled: true + id: debugNextButton + onClicked: { + dbg.next() + } + text: "Next" + } + + Button { + id: debugContinueButton + onClicked: { + dbg.continue() + } + text: "Continue" + } + } + + + ComboBox { + id: snippets + anchors.right: parent.right + model: ListModel { + ListElement { text: "Snippets" ; value: "" } + ListElement { text: "Call Contract" ; value: "var[2] in;\nvar ret;\n\nin[0] = \"arg1\"\nin[1] = 0xdeadbeef\n\nvar success = call(0x0c542ddea93dae0c2fcb2cf175f03ad80d6be9a0, 0, 7000, in, ret)\n\nreturn ret" } + } + onCurrentIndexChanged: { + if(currentIndex != 0) { + var code = snippets.model.get(currentIndex).value; + codeEditor.insert(codeEditor.cursorPosition, code) + } + } + } + + } + + function debugCurrent() { + dbg.debug(txValue.text, txGas.text, txGasPrice.text, codeEditor.text, rawDataField.text) + } + + function setAsm(asm) { + asmModel.append({asm: asm}) + } + + function clearAsm() { + asmModel.clear() + } + + function setInstruction(num) { + asmTableView.selection.clear() + asmTableView.selection.select(num) + asmTableView.positionViewAtRow(num, ListView.Center) + } + + function setMem(mem) { + memModel.append({num: mem.num, value: mem.value}) + } + function clearMem(){ + memModel.clear() + } + + function setStack(stack) { + stackModel.append({value: stack}) + } + function addDebugMessage(message){ + debuggerLog.append({value: message}) + } + + function clearStack() { + stackModel.clear() + } + + function clearStorage() { + storageModel.clear() + } + + function setStorage(storage) { + storageModel.append({key: storage.key, value: storage.value}) + } + + function setLog(msg) { + // Remove first item once we've reached max log items + if(logModel.count > 250) { + logModel.remove(0) + } + + if(msg.len != 0) { + if(logTableView.flickableItem.atYEnd) { + logModel.append({message: msg}) + logTableView.positionViewAtRow(logTableView.rowCount - 1, ListView.Contain) + } else { + logModel.append({message: msg}) + } + } + } + + function clearLog() { + logModel.clear() + } } diff --git a/mist/assets/ext/html_messaging.js b/mist/assets/ext/html_messaging.js new file mode 100644 index 000000000000..cf6d72cfa424 --- /dev/null +++ b/mist/assets/ext/html_messaging.js @@ -0,0 +1,149 @@ +// The magic return variable. The magic return variable will be set during the execution of the QML call. +(function(window) { + function message(type, data) { + document.title = JSON.stringify({type: type, data: data}); + + return window.____returnData; + } + + function isPromise(o) { + return typeof o === "object" && o.then + } + + window.eth = { + _callbacks: {}, + _onCallbacks: {}, + prototype: Object(), + + coinbase: function() { + return new Promise(function(resolve, reject) { + postData({call: "getCoinBase"}, function(coinbase) { + resolve(coinbase); + }); + }); + }, + + block: function(numberOrHash) { + return new Promise(function(resolve, reject) { + var func; + if(typeof numberOrHash == "string") { + func = "getBlockByHash"; + } else { + func = "getBlockByNumber"; + } + + postData({call: func, args: [numberOrHash]}, function(block) { + if(block) + resolve(block); + else + reject("not found"); + + }); + }); + }, + + transact: function(params) { + if(params === undefined) { + params = {}; + } + + if(params.endowment !== undefined) + params.value = params.endowment; + if(params.code !== undefined) + params.data = params.code; + + + var promises = [] + if(isPromise(params.to)) { + promises.push(params.to.then(function(_to) { params.to = _to; })); + } + if(isPromise(params.from)) { + promises.push(params.from.then(function(_from) { params.from = _from; })); + } + + if(isPromise(params.data)) { + promises.push(params.data.then(function(_code) { params.data = _code; })); + } else { + if(typeof params.data === "object") { + data = ""; + for(var i = 0; i < params.data.length; i++) { + data += params.data[i] + } + } else { + data = params.data; + } + } + + // Make sure everything is string + var fields = ["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(); + } + + // Load promises then call the last "transact". + return Q.all(promises).then(function() { + return new Promise(function(resolve, reject) { + postData({call: "transact", args: params}, function(data) { + if(data[1]) + reject(data[0]); + else + resolve(data[0]); + }); + }); + }) + }, + + compile: function(code) { + return new Promise(function(resolve, reject) { + postData({call: "compile", args: [code]}, function(data) { + if(data[1]) + reject(data[0]); + else + resolve(data[0]); + }); + }); + }, + + key: function() { + return new Promise(function(resolve, reject) { + postData({call: "getKey"}, function(k) { + resolve(k); + }); + }); + } + }; + + 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]; + } + } + } + } +})(this); diff --git a/mist/assets/ext/pre.js b/mist/assets/ext/pre.js index 3e8a534e9b61..528149f6be93 100644 --- a/mist/assets/ext/pre.js +++ b/mist/assets/ext/pre.js @@ -35,3 +35,7 @@ navigator.qt.onmessage = function(ev) { } } } + +if(typeof(Promise) === "undefined") { + window.Promise = Q.Promise; +} diff --git a/mist/assets/ext/q.js b/mist/assets/ext/q.js new file mode 100644 index 000000000000..23c4245eeccb --- /dev/null +++ b/mist/assets/ext/q.js @@ -0,0 +1,1909 @@ +// vim:ts=4:sts=4:sw=4: +/*! + * + * Copyright 2009-2012 Kris Kowal under the terms of the MIT + * license found at http://github.com/kriskowal/q/raw/master/LICENSE + * + * With parts by Tyler Close + * Copyright 2007-2009 Tyler Close under the terms of the MIT X license found + * at http://www.opensource.org/licenses/mit-license.html + * Forked at ref_send.js version: 2009-05-11 + * + * With parts by Mark Miller + * Copyright (C) 2011 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +(function (definition) { + // Turn off strict mode for this function so we can assign to global.Q + /* jshint strict: false */ + + // This file will function properly as a