Przeglądaj źródła

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.
obscuren 11 lat temu
rodzic
commit
64c2550b31
4 zmienionych plików z 286 dodań i 57 usunięć
  1. 175 0
      ethereal/ui/ext_app.go
  2. 73 0
      ethereal/ui/html_container.go
  3. 28 5
      ethereal/ui/library.go
  4. 10 52
      ethereal/ui/ui_lib.go

+ 175 - 0
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
+}

+ 73 - 0
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()
+}

+ 28 - 5
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 {

+ 10 - 52
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) {