aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorobscuren <geffobscura@gmail.com>2014-04-30 07:44:02 +0800
committerobscuren <geffobscura@gmail.com>2014-04-30 07:44:02 +0800
commit64c2550b3154df7f2c75dda559d91046cb559ffd (patch)
tree4dfbaeac371c73b3518566bb0c2e2a141b7d4b49
parent922974c760278b6d49cb6f286b663d60f77d5248 (diff)
downloadgo-tangerine-64c2550b3154df7f2c75dda559d91046cb559ffd.tar.gz
go-tangerine-64c2550b3154df7f2c75dda559d91046cb559ffd.tar.zst
go-tangerine-64c2550b3154df7f2c75dda559d91046cb559ffd.zip
Split off External applications from main library
External applications now accept containers which function as the frontend where the ExtApplication functions as the backend. Containers execute within their own engine and have their own context and are destroyed when released.
-rw-r--r--ethereal/ui/ext_app.go175
-rw-r--r--ethereal/ui/html_container.go73
-rw-r--r--ethereal/ui/library.go33
-rw-r--r--ethereal/ui/ui_lib.go62
4 files changed, 286 insertions, 57 deletions
diff --git a/ethereal/ui/ext_app.go b/ethereal/ui/ext_app.go
new file mode 100644
index 000000000..b90a2192c
--- /dev/null
+++ b/ethereal/ui/ext_app.go
@@ -0,0 +1,175 @@
+package ethui
+
+import (
+ "fmt"
+ "github.com/ethereum/eth-go"
+ "github.com/ethereum/eth-go/ethchain"
+ "github.com/ethereum/eth-go/ethutil"
+ "github.com/go-qml/qml"
+ "math/big"
+)
+
+type AppContainer interface {
+ Create() error
+ Destroy()
+
+ Window() *qml.Window
+ Engine() *qml.Engine
+
+ NewBlock(*ethchain.Block)
+ ObjectChanged(*ethchain.StateObject)
+ StorageChanged(*ethchain.StateObject, []byte, *big.Int)
+}
+
+type ExtApplication struct {
+ *QEthereum
+
+ blockChan chan ethutil.React
+ changeChan chan ethutil.React
+ quitChan chan bool
+
+ container AppContainer
+ lib *UiLib
+ registeredEvents []string
+}
+
+func NewExtApplication(container AppContainer, lib *UiLib) *ExtApplication {
+ app := &ExtApplication{
+ NewQEthereum(lib.eth),
+ make(chan ethutil.React, 1),
+ make(chan ethutil.React, 1),
+ make(chan bool),
+ container,
+ lib,
+ nil,
+ }
+
+ return app
+}
+
+func (app *ExtApplication) run() {
+ // Set the "eth" api on to the containers context
+ context := app.container.Engine().Context()
+ context.SetVar("eth", app)
+ context.SetVar("ui", app.lib)
+
+ err := app.container.Create()
+ if err != nil {
+ fmt.Println(err)
+
+ return
+ }
+
+ // Call the main loop
+ go app.mainLoop()
+
+ // Subscribe to events
+ reactor := app.lib.eth.Reactor()
+ reactor.Subscribe("newBlock", app.blockChan)
+
+ win := app.container.Window()
+ win.Show()
+ win.Wait()
+
+ app.stop()
+}
+
+func (app *ExtApplication) stop() {
+ // Clean up
+ reactor := app.lib.eth.Reactor()
+ reactor.Unsubscribe("newBlock", app.blockChan)
+ for _, event := range app.registeredEvents {
+ reactor.Unsubscribe(event, app.changeChan)
+ }
+
+ // Kill the main loop
+ app.quitChan <- true
+
+ close(app.blockChan)
+ close(app.quitChan)
+ close(app.changeChan)
+
+ app.container.Destroy()
+}
+
+func (app *ExtApplication) mainLoop() {
+out:
+ for {
+ select {
+ case <-app.quitChan:
+ break out
+ case block := <-app.blockChan:
+ if block, ok := block.Resource.(*ethchain.Block); ok {
+ app.container.NewBlock(block)
+ }
+ case object := <-app.changeChan:
+ if stateObject, ok := object.Resource.(*ethchain.StateObject); ok {
+ app.container.ObjectChanged(stateObject)
+ } else if _, ok := object.Resource.(*big.Int); ok {
+ //
+ }
+ }
+ }
+
+}
+
+func (app *ExtApplication) Watch(addr, storageAddr string) {
+ var event string
+ if len(storageAddr) == 0 {
+ event = "storage:" + string(ethutil.FromHex(addr)) + ":" + string(ethutil.FromHex(storageAddr))
+ app.lib.eth.Reactor().Subscribe(event, app.changeChan)
+ } else {
+ event = "object:" + string(ethutil.FromHex(addr))
+ app.lib.eth.Reactor().Subscribe(event, app.changeChan)
+ }
+
+ app.registeredEvents = append(app.registeredEvents, event)
+}
+
+type QEthereum struct {
+ stateManager *ethchain.StateManager
+ blockChain *ethchain.BlockChain
+ txPool *ethchain.TxPool
+}
+
+func NewQEthereum(eth *eth.Ethereum) *QEthereum {
+ return &QEthereum{
+ eth.StateManager(),
+ eth.BlockChain(),
+ eth.TxPool(),
+ }
+}
+
+func (lib *QEthereum) GetBlock(hexHash string) *QBlock {
+ hash := ethutil.FromHex(hexHash)
+
+ block := lib.blockChain.GetBlock(hash)
+
+ return &QBlock{Number: int(block.BlockInfo().Number), Hash: ethutil.Hex(block.Hash())}
+}
+
+func (lib *QEthereum) GetKey() string {
+ return ethutil.Hex(ethutil.Config.Db.GetKeys()[0].Address())
+}
+
+func (lib *QEthereum) GetStateObject(address string) *Contract {
+ stateObject := lib.stateManager.ProcState().GetContract(ethutil.FromHex(address))
+ if stateObject != nil {
+ return NewContract(stateObject)
+ }
+
+ // See GetStorage for explanation on "nil"
+ return NewContract(nil)
+}
+
+func (lib *QEthereum) Watch(addr, storageAddr string) {
+ // lib.stateManager.Watch(ethutil.FromHex(addr), ethutil.FromHex(storageAddr))
+}
+
+func (lib *QEthereum) CreateTx(recipient, valueStr, gasStr, gasPriceStr, dataStr string) (string, error) {
+ return lib.Transact(recipient, valueStr, gasStr, gasPriceStr, dataStr)
+}
+
+func (lib *QEthereum) Transact(recipient, valueStr, gasStr, gasPriceStr, dataStr string) (string, error) {
+ return "", nil
+}
diff --git a/ethereal/ui/html_container.go b/ethereal/ui/html_container.go
new file mode 100644
index 000000000..8e3ef0fc7
--- /dev/null
+++ b/ethereal/ui/html_container.go
@@ -0,0 +1,73 @@
+package ethui
+
+import (
+ "errors"
+ "github.com/ethereum/eth-go/ethchain"
+ "github.com/ethereum/eth-go/ethutil"
+ "github.com/go-qml/qml"
+ "math/big"
+ "path/filepath"
+)
+
+type HtmlApplication struct {
+ win *qml.Window
+ webView qml.Object
+ engine *qml.Engine
+ lib *UiLib
+ path string
+}
+
+func NewHtmlApplication(path string, lib *UiLib) *HtmlApplication {
+ engine := qml.NewEngine()
+
+ return &HtmlApplication{engine: engine, lib: lib, path: path}
+
+}
+
+func (app *HtmlApplication) Create() error {
+ component, err := app.engine.LoadFile(app.lib.AssetPath("qml/webapp.qml"))
+ if err != nil {
+ return err
+ }
+
+ if filepath.Ext(app.path) == "eth" {
+ return errors.New("Ethereum package not yet supported")
+
+ // TODO
+ ethutil.OpenPackage(app.path)
+ }
+
+ win := component.CreateWindow(nil)
+ win.Set("url", app.path)
+ webView := win.ObjectByName("webView")
+
+ app.win = win
+ app.webView = webView
+
+ return nil
+}
+
+func (app *HtmlApplication) Engine() *qml.Engine {
+ return app.engine
+}
+
+func (app *HtmlApplication) Window() *qml.Window {
+ return app.win
+}
+
+func (app *HtmlApplication) NewBlock(block *ethchain.Block) {
+ b := &QBlock{Number: int(block.BlockInfo().Number), Hash: ethutil.Hex(block.Hash())}
+ app.webView.Call("onNewBlockCb", b)
+}
+
+func (app *HtmlApplication) ObjectChanged(stateObject *ethchain.StateObject) {
+ app.webView.Call("onObjectChangeCb", NewQStateObject(stateObject))
+}
+
+func (app *HtmlApplication) StorageChanged(stateObject *ethchain.StateObject, addr []byte, value *big.Int) {
+ app.webView.Call("onStorageChangeCb", nil)
+}
+
+func (app *HtmlApplication) Destroy() {
+ app.engine.Destroy()
+}
diff --git a/ethereal/ui/library.go b/ethereal/ui/library.go
index 7f667f2c1..ec5f29f95 100644
--- a/ethereal/ui/library.go
+++ b/ethereal/ui/library.go
@@ -19,9 +19,24 @@ func NewContract(object *ethchain.StateObject) *Contract {
}
func (c *Contract) GetStorage(address string) string {
- val := c.object.GetMem(ethutil.Big("0x" + address))
+ // Because somehow, even if you return nil to QML it
+ // still has some magical object so we can't rely on
+ // undefined or null at the QML side
+ if c.object != nil {
+ val := c.object.GetMem(ethutil.Big("0x" + address))
- return val.BigInt().String()
+ return val.BigInt().String()
+ }
+
+ return ""
+}
+
+func (c *Contract) Value() string {
+ if c.object != nil {
+ return c.object.Amount.String()
+ }
+
+ return ""
}
type EthLib struct {
@@ -63,15 +78,23 @@ func (lib *EthLib) GetKey() string {
func (lib *EthLib) GetStateObject(address string) *Contract {
stateObject := lib.stateManager.ProcState().GetContract(ethutil.FromHex(address))
+ if stateObject != nil {
+ return NewContract(stateObject)
+ }
- return NewContract(stateObject)
+ // See GetStorage for explanation on "nil"
+ return NewContract(nil)
}
-func (lib *EthLib) Watch(addr string) {
- lib.stateManager.Watch(ethutil.FromHex(addr))
+func (lib *EthLib) Watch(addr, storageAddr string) {
+ // lib.stateManager.Watch(ethutil.FromHex(addr), ethutil.FromHex(storageAddr))
}
func (lib *EthLib) CreateTx(recipient, valueStr, gasStr, gasPriceStr, dataStr string) (string, error) {
+ return lib.Transact(recipient, valueStr, gasStr, gasPriceStr, dataStr)
+}
+
+func (lib *EthLib) Transact(recipient, valueStr, gasStr, gasPriceStr, dataStr string) (string, error) {
var hash []byte
var contractCreation bool
if len(recipient) == 0 {
diff --git a/ethereal/ui/ui_lib.go b/ethereal/ui/ui_lib.go
index 07cd0ac8a..0feb522bc 100644
--- a/ethereal/ui/ui_lib.go
+++ b/ethereal/ui/ui_lib.go
@@ -6,9 +6,9 @@ import (
"github.com/ethereum/eth-go"
"github.com/ethereum/eth-go/ethchain"
"github.com/ethereum/eth-go/ethutil"
- "github.com/obscuren/mutan"
"github.com/ethereum/go-ethereum/utils"
"github.com/go-qml/qml"
+ "github.com/obscuren/mutan"
"os"
"path"
"path/filepath"
@@ -53,60 +53,18 @@ func (ui *UiLib) Open(path string) {
}
func (ui *UiLib) OpenHtml(path string) {
- component, err := ui.engine.LoadFile(ui.AssetPath("qml/webapp.qml"))
- if err != nil {
- ethutil.Config.Log.Debugln(err)
+ container := NewHtmlApplication(path, ui)
+ app := NewExtApplication(container, ui)
- return
- }
- win := component.CreateWindow(nil)
- if filepath.Ext(path) == "eth" {
- fmt.Println("Ethereum package not yet supported")
-
- return
+ go app.run()
+}
- // TODO
- ethutil.OpenPackage(path)
+func (ui *UiLib) Watch(addr, storageAddr string) {
+ if len(storageAddr) == 0 {
+ ui.eth.Reactor().Subscribe("storage:"+string(ethutil.FromHex(addr))+":"+string(ethutil.FromHex(storageAddr)), nil)
+ } else {
+ ui.eth.Reactor().Subscribe("object:"+string(ethutil.FromHex(addr)), nil)
}
- win.Set("url", path)
-
- webView := win.ObjectByName("webView")
- go func() {
- blockChan := make(chan ethutil.React, 1)
- addrChan := make(chan ethutil.React, 1)
- quitChan := make(chan bool)
-
- go func() {
- out:
- for {
- select {
- case <-quitChan:
- ui.eth.Reactor().Unsubscribe("newBlock", blockChan)
- break out
- case block := <-blockChan:
- if block, ok := block.Resource.(*ethchain.Block); ok {
- b := &QBlock{Number: int(block.BlockInfo().Number), Hash: ethutil.Hex(block.Hash())}
- webView.Call("onNewBlockCb", b)
- }
- case stateObject := <-addrChan:
- if stateObject, ok := stateObject.Resource.(*ethchain.StateObject); ok {
- webView.Call("onObjectChangeCb", NewQStateObject(stateObject))
- }
- }
- }
-
- // Clean up
- close(blockChan)
- close(quitChan)
- }()
- ui.eth.Reactor().Subscribe("newBlock", blockChan)
- ui.eth.Reactor().Subscribe("addressChanged", addrChan)
-
- win.Show()
- win.Wait()
-
- quitChan <- true
- }()
}
func (ui *UiLib) Muted(content string) {