From 1f2547b8a7cfe100f64428d20f4bcf95eb9ecc5c Mon Sep 17 00:00:00 2001 From: obscuren Date: Sat, 22 Mar 2014 01:02:24 +0100 Subject: Major re-organisation. The Ethereum node and Gui are now separated. --- ethereal/Makefile | 21 ++ ethereal/assets/facet.png | Bin 0 -> 27302 bytes ethereal/assets/net.png | Bin 0 -> 4669 bytes ethereal/assets/network.png | Bin 0 -> 2900 bytes ethereal/assets/new.png | Bin 0 -> 4776 bytes ethereal/assets/qml/test_app.qml | 35 +++ ethereal/assets/qml/transactions.qml | 9 + ethereal/assets/qml/wallet.qml | 408 +++++++++++++++++++++++++++++++++++ ethereal/assets/tx.png | Bin 0 -> 4070 bytes ethereal/config.go | 34 +++ ethereal/ethereum.go | 110 ++++++++++ ethereal/ui/gui.go | 218 +++++++++++++++++++ ethereal/ui/library.go | 60 ++++++ ethereal/ui/ui_lib.go | 76 +++++++ 14 files changed, 971 insertions(+) create mode 100644 ethereal/Makefile create mode 100644 ethereal/assets/facet.png create mode 100644 ethereal/assets/net.png create mode 100644 ethereal/assets/network.png create mode 100644 ethereal/assets/new.png create mode 100644 ethereal/assets/qml/test_app.qml create mode 100644 ethereal/assets/qml/transactions.qml create mode 100644 ethereal/assets/qml/wallet.qml create mode 100644 ethereal/assets/tx.png create mode 100644 ethereal/config.go create mode 100644 ethereal/ethereum.go create mode 100644 ethereal/ui/gui.go create mode 100644 ethereal/ui/library.go create mode 100644 ethereal/ui/ui_lib.go (limited to 'ethereal') diff --git a/ethereal/Makefile b/ethereal/Makefile new file mode 100644 index 000000000..02d127963 --- /dev/null +++ b/ethereal/Makefile @@ -0,0 +1,21 @@ +UNAME = $(shell uname) + +# Default is building +all: + go install + +install: +# Linux build +ifeq ($(UNAME),Linux) + mkdir -p /usr/local/ethereal + files=(net.png network.png new.png tx.png) + for file in "${files[@]}"; do + cp $file /usr/share/ethereal + done + cp -r qml /usr/share/ethereal/qml + cp $GOPATH/bin/go-ethereum /usr/local/bin/ethereal +endif +# OS X build +ifeq ($(UNAME),Darwin) + # Execute py script +endif diff --git a/ethereal/assets/facet.png b/ethereal/assets/facet.png new file mode 100644 index 000000000..49a266e96 Binary files /dev/null and b/ethereal/assets/facet.png differ diff --git a/ethereal/assets/net.png b/ethereal/assets/net.png new file mode 100644 index 000000000..65a20ea00 Binary files /dev/null and b/ethereal/assets/net.png differ diff --git a/ethereal/assets/network.png b/ethereal/assets/network.png new file mode 100644 index 000000000..0a9ffe2ec Binary files /dev/null and b/ethereal/assets/network.png differ diff --git a/ethereal/assets/new.png b/ethereal/assets/new.png new file mode 100644 index 000000000..e80096748 Binary files /dev/null and b/ethereal/assets/new.png differ diff --git a/ethereal/assets/qml/test_app.qml b/ethereal/assets/qml/test_app.qml new file mode 100644 index 000000000..aace4e881 --- /dev/null +++ b/ethereal/assets/qml/test_app.qml @@ -0,0 +1,35 @@ +import QtQuick 2.0 +import QtQuick.Controls 1.0; +import QtQuick.Layouts 1.0; +import Ethereum 1.0 + +ApplicationWindow { + minimumWidth: 500 + maximumWidth: 500 + maximumHeight: 100 + minimumHeight: 100 + + title: "Ethereum Dice" + + TextField { + id: textField + anchors.verticalCenter: parent.verticalCenter + anchors.horizontalCenter: parent.horizontalCenter + placeholderText: "Amount" + } + Label { + id: txHash + anchors.bottom: textField.top + anchors.bottomMargin: 5 + anchors.horizontalCenter: parent.horizontalCenter + } + Button { + anchors.top: textField.bottom + anchors.horizontalCenter: parent.horizontalCenter + anchors.topMargin: 5 + text: "Place bet" + onClicked: { + txHash.text = eth.createTx("e6716f9544a56c530d868e4bfbacb172315bdead", textField.text) + } + } +} diff --git a/ethereal/assets/qml/transactions.qml b/ethereal/assets/qml/transactions.qml new file mode 100644 index 000000000..e9a035a85 --- /dev/null +++ b/ethereal/assets/qml/transactions.qml @@ -0,0 +1,9 @@ +import QtQuick 2.0 +import QtQuick.Controls 1.0; +import QtQuick.Layouts 1.0; + +Rectangle { + id: transactionView + visible: false + Text { text: "TX VIEW" } +} diff --git a/ethereal/assets/qml/wallet.qml b/ethereal/assets/qml/wallet.qml new file mode 100644 index 000000000..7fc7f5447 --- /dev/null +++ b/ethereal/assets/qml/wallet.qml @@ -0,0 +1,408 @@ +import QtQuick 2.0 +import QtQuick.Controls 1.0; +import QtQuick.Layouts 1.0; +import QtQuick.Dialogs 1.0; +import QtQuick.Window 2.1; +import QtQuick.Controls.Styles 1.1 +import Ethereum 1.0 + +ApplicationWindow { + id: root + + width: 900 + height: 600 + minimumHeight: 300 + + title: "Ethereal" + + MenuBar { + Menu { + title: "File" + MenuItem { + text: "Import App" + shortcut: "Ctrl+o" + onTriggered: openAppDialog.open() + } + } + + Menu { + title: "Network" + MenuItem { + text: "Add Peer" + shortcut: "Ctrl+p" + onTriggered: { + addPeerWin.visible = true + } + } + + MenuItem { + text: "Start" + onTriggered: ui.connect() + } + } + + Menu { + title: "Help" + MenuItem { + text: "About" + onTriggered: { + aboutWin.visible = true + } + } + } + + } + + + property var blockModel: ListModel { + id: blockModel + } + + function setView(view) { + networkView.visible = false + historyView.visible = false + newTxView.visible = false + view.visible = true + //root.title = "Ethereal - " = view.title + } + + SplitView { + anchors.fill: parent + resizing: false + + Rectangle { + id: menu + Layout.minimumWidth: 80 + Layout.maximumWidth: 80 + anchors.bottom: parent.bottom + anchors.top: parent.top + //color: "#D9DDE7" + color: "#252525" + + ColumnLayout { + y: 50 + anchors.left: parent.left + anchors.right: parent.right + height: 200 + Image { + source: ui.assetPath("tx.png") + anchors.horizontalCenter: parent.horizontalCenter + MouseArea { + anchors.fill: parent + onClicked: { + setView(historyView) + } + } + } + Image { + source: ui.assetPath("new.png") + anchors.horizontalCenter: parent.horizontalCenter + MouseArea { + anchors.fill: parent + onClicked: { + setView(newTxView) + } + } + } + Image { + source: ui.assetPath("net.png") + anchors.horizontalCenter: parent.horizontalCenter + MouseArea { + anchors.fill: parent + onClicked: { + setView(networkView) + } + } + } + } + } + + Rectangle { + id: mainView + color: "#00000000" + anchors.right: parent.right + anchors.left: menu.right + anchors.bottom: parent.bottom + anchors.top: parent.top + + property var txModel: ListModel { + id: txModel + } + + Rectangle { + id: historyView + anchors.fill: parent + + property var title: "Transactions" + TableView { + id: txTableView + anchors.fill: parent + TableViewColumn{ role: "value" ; title: "Value" ; width: 100 } + TableViewColumn{ role: "address" ; title: "Address" ; width: 430 } + + model: txModel + } + } + + Rectangle { + id: newTxView + property var title: "New transaction" + visible: false + anchors.fill: parent + color: "#00000000" + + ColumnLayout { + width: 400 + anchors.left: parent.left + anchors.top: parent.top + anchors.leftMargin: 5 + anchors.topMargin: 5 + TextField { + id: txAmount + width: 200 + placeholderText: "Amount" + } + + TextField { + id: txReceiver + placeholderText: "Receiver Address (or empty for contract)" + Layout.fillWidth: true + } + + Label { + text: "Transaction data" + } + TextArea { + id: codeView + anchors.topMargin: 5 + Layout.fillWidth: true + width: parent.width /2 + } + + Button { + text: "Send" + onClicked: { + console.log(eth.createTx(txReceiver.text, txAmount.text, codeView.text)) + } + } + } + } + + + Rectangle { + id: networkView + property var title: "Network" + visible: false + anchors.fill: parent + + TableView { + id: blockTable + width: parent.width + anchors.top: parent.top + anchors.bottom: logView.top + TableViewColumn{ role: "number" ; title: "#" ; width: 100 } + TableViewColumn{ role: "hash" ; title: "Hash" ; width: 560 } + + model: blockModel + + /* + onDoubleClicked: { + popup.visible = true + popup.block = eth.getBlock(blockModel.get(row).hash) + popup.hashLabel.text = popup.block.hash + } + */ + } + + property var logModel: ListModel { + id: logModel + } + + TableView { + id: logView + width: parent.width + height: 150 + anchors.bottom: parent.bottom + TableViewColumn{ role: "description" ; title: "log" } + + model: logModel + } + } + + /* + signal addPlugin(string name) + Component { + id: pluginWindow + Rectangle { + anchors.fill: parent + Label { + id: pluginTitle + anchors.centerIn: parent + text: "Hello world" + } + Component.onCompleted: setView(this) + } + } + + onAddPlugin: { + var pluginWin = pluginWindow.createObject(mainView) + console.log(pluginWin) + pluginWin.pluginTitle.text = "Test" + } + */ + } + } + + FileDialog { + id: openAppDialog + title: "Open QML Application" + onAccepted: { + ui.open(openAppDialog.fileUrl.toString()) + } + } + + statusBar: StatusBar { + RowLayout { + anchors.fill: parent + Button { + property var enabled: true + id: connectButton + onClicked: { + if(this.enabled) { + ui.connect(this) + } + } + text: "Connect" + } + + Button { + id: importAppButton + anchors.left: connectButton.right + anchors.leftMargin: 5 + onClicked: openAppDialog.open() + text: "Import App" + } + + Label { + anchors.left: importAppButton.right + anchors.leftMargin: 5 + id: walletValueLabel + } + + Label { + anchors.right: peerImage.left + anchors.rightMargin: 5 + id: peerLabel + font.pixelSize: 8 + text: "0 / 0" + } + Image { + id: peerImage + anchors.right: parent.right + width: 10; height: 10 + source: ui.assetPath("network.png") + } + } + } + + Window { + id: popup + visible: false + property var block + Label { + id: hashLabel + anchors.horizontalCenter: parent.horizontalCenter + anchors.verticalCenter: parent.verticalCenter + } + } + + 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: { + ui.connectToPeer(addrField.text) + addPeerWin.visible = false + } + } + Button { + anchors.left: addrField.right + anchors.verticalCenter: parent.verticalCenter + anchors.leftMargin: 5 + text: "Add" + onClicked: { + ui.connectToPeer(addrField.text) + addPeerWin.visible = false + } + } + Component.onCompleted: { + addrField.focus = true + } + } + + Window { + id: aboutWin + visible: false + title: "About" + minimumWidth: 350 + maximumWidth: 350 + maximumHeight: 200 + minimumHeight: 200 + + Image { + id: aboutIcon + height: 150 + width: 150 + fillMode: Image.PreserveAspectFit + smooth: true + source: ui.assetPath("facet.png") + x: 10 + y: 10 + } + + Text { + anchors.left: aboutIcon.right + anchors.leftMargin: 10 + font.pointSize: 12 + text: "

Ethereum(Go)


Development

Jeffrey Wilcke

Binary Distribution

Jarrad Hope
" + } + + } + + function loadPlugin(name) { + console.log("Loading plugin" + name) + mainView.addPlugin(name) + } + + function setWalletValue(value) { + walletValueLabel.text = value + } + + function addTx(tx) { + txModel.insert(0, {hash: tx.hash, address: tx.address, value: tx.value}) + } + + function addBlock(block) { + blockModel.insert(0, {number: block.number, hash: block.hash}) + } + + function addLog(str) { + if(str.len != 0) { + logModel.append({description: str}) + } + } + + function setPeers(text) { + peerLabel.text = text + } +} diff --git a/ethereal/assets/tx.png b/ethereal/assets/tx.png new file mode 100644 index 000000000..62204c315 Binary files /dev/null and b/ethereal/assets/tx.png differ diff --git a/ethereal/config.go b/ethereal/config.go new file mode 100644 index 000000000..a534bb182 --- /dev/null +++ b/ethereal/config.go @@ -0,0 +1,34 @@ +package main + +import ( + "flag" +) + +var StartConsole bool +var StartMining bool +var UseUPnP bool +var OutboundPort string +var ShowGenesis bool +var AddPeer string +var MaxPeer int +var GenAddr bool +var UseSeed bool +var ImportKey string +var ExportKey bool +var DataDir string + +func Init() { + flag.BoolVar(&StartConsole, "c", false, "debug and testing console") + flag.BoolVar(&StartMining, "m", false, "start dagger mining") + flag.BoolVar(&ShowGenesis, "g", false, "prints genesis header and exits") + flag.BoolVar(&UseUPnP, "upnp", false, "enable UPnP support") + flag.BoolVar(&UseSeed, "seed", true, "seed peers") + flag.BoolVar(&GenAddr, "genaddr", false, "create a new priv/pub key") + flag.BoolVar(&ExportKey, "export", false, "export private key") + flag.StringVar(&OutboundPort, "p", "30303", "listening port") + flag.StringVar(&DataDir, "dir", ".ethereal", "ethereum data directory") + flag.StringVar(&ImportKey, "import", "", "imports the given private key (hex)") + flag.IntVar(&MaxPeer, "x", 5, "maximum desired peers") + + flag.Parse() +} diff --git a/ethereal/ethereum.go b/ethereal/ethereum.go new file mode 100644 index 000000000..618d2b00f --- /dev/null +++ b/ethereal/ethereum.go @@ -0,0 +1,110 @@ +package main + +import ( + "fmt" + "github.com/ethereum/eth-go" + "github.com/ethereum/eth-go/ethchain" + "github.com/ethereum/eth-go/ethutil" + "github.com/ethereum/go-ethereum/ethereal/ui" + "github.com/ethereum/go-ethereum/utils" + "github.com/niemeyer/qml" + "log" + "os" + "os/signal" + "runtime" +) + +const Debug = true + +// Register interrupt handlers so we can stop the ethereum +func RegisterInterupts(s *eth.Ethereum) { + // Buffered chan of one is enough + c := make(chan os.Signal, 1) + // Notify about interrupts for now + signal.Notify(c, os.Interrupt) + go func() { + for sig := range c { + fmt.Printf("Shutting down (%v) ... \n", sig) + + s.Stop() + } + }() +} + +func main() { + Init() + + qml.Init(nil) + + runtime.GOMAXPROCS(runtime.NumCPU()) + + ethchain.InitFees() + ethutil.ReadConfig(DataDir) + ethutil.Config.Seed = UseSeed + + // Instantiated a eth stack + ethereum, err := eth.New(eth.CapDefault, UseUPnP) + if err != nil { + log.Println("eth start err:", err) + return + } + ethereum.Port = OutboundPort + + if GenAddr { + fmt.Println("This action overwrites your old private key. Are you sure? (y/n)") + + var r string + fmt.Scanln(&r) + for ; ; fmt.Scanln(&r) { + if r == "n" || r == "y" { + break + } else { + fmt.Printf("Yes or no?", r) + } + } + + if r == "y" { + utils.CreateKeyPair(true) + } + os.Exit(0) + } else { + if len(ImportKey) > 0 { + fmt.Println("This action overwrites your old private key. Are you sure? (y/n)") + var r string + fmt.Scanln(&r) + for ; ; fmt.Scanln(&r) { + if r == "n" || r == "y" { + break + } else { + fmt.Printf("Yes or no?", r) + } + } + + if r == "y" { + utils.ImportPrivateKey(ImportKey) + os.Exit(0) + } + } else { + utils.CreateKeyPair(false) + } + } + + if ExportKey { + key := ethutil.Config.Db.GetKeys()[0] + fmt.Printf("%x\n", key.PrivateKey) + os.Exit(0) + } + + if ShowGenesis { + fmt.Println(ethereum.BlockChain().Genesis()) + os.Exit(0) + } + + log.Printf("Starting Ethereum v%s\n", ethutil.Config.Ver) + + // Set the max peers + ethereum.MaxPeers = MaxPeer + + gui := ethui.New(ethereum) + gui.Start() +} diff --git a/ethereal/ui/gui.go b/ethereal/ui/gui.go new file mode 100644 index 000000000..c8f4bedab --- /dev/null +++ b/ethereal/ui/gui.go @@ -0,0 +1,218 @@ +package ethui + +import ( + "bytes" + "encoding/hex" + "fmt" + "github.com/ethereum/eth-go" + "github.com/ethereum/eth-go/ethchain" + "github.com/ethereum/eth-go/ethdb" + "github.com/ethereum/eth-go/ethutil" + "github.com/niemeyer/qml" + "math/big" + "strings" +) + +// Block interface exposed to QML +type Block struct { + Number int + Hash string +} + +type Tx struct { + Value, Hash, Address string +} + +func NewTxFromTransaction(tx *ethchain.Transaction) *Tx { + hash := hex.EncodeToString(tx.Hash()) + sender := hex.EncodeToString(tx.Recipient) + + return &Tx{Hash: hash, Value: ethutil.CurrencyToString(tx.Value), Address: sender} +} + +// Creates a new QML Block from a chain block +func NewBlockFromBlock(block *ethchain.Block) *Block { + info := block.BlockInfo() + hash := hex.EncodeToString(block.Hash()) + + return &Block{Number: int(info.Number), Hash: hash} +} + +type Gui struct { + // The main application window + win *qml.Window + // QML Engine + engine *qml.Engine + component *qml.Common + // The ethereum interface + eth *eth.Ethereum + + // The public Ethereum library + lib *EthLib + + txDb *ethdb.LDBDatabase + + addr []byte +} + +// Create GUI, but doesn't start it +func New(ethereum *eth.Ethereum) *Gui { + lib := &EthLib{stateManager: ethereum.StateManager(), blockChain: ethereum.BlockChain(), txPool: ethereum.TxPool()} + db, err := ethdb.NewLDBDatabase("tx_database") + if err != nil { + panic(err) + } + + key := ethutil.Config.Db.GetKeys()[0] + addr := key.Address() + + ethereum.StateManager().WatchAddr(addr) + + return &Gui{eth: ethereum, lib: lib, txDb: db, addr: addr} +} + +func (ui *Gui) Start() { + defer ui.txDb.Close() + + // Register ethereum functions + qml.RegisterTypes("Ethereum", 1, 0, []qml.TypeSpec{{ + Init: func(p *Block, obj qml.Object) { p.Number = 0; p.Hash = "" }, + }, { + Init: func(p *Tx, obj qml.Object) { p.Value = ""; p.Hash = ""; p.Address = "" }, + }}) + + ethutil.Config.SetClientString(fmt.Sprintf("/Ethereal v%s", "0.1")) + ethutil.Config.Log.Infoln("[GUI] Starting GUI") + // Create a new QML engine + ui.engine = qml.NewEngine() + context := ui.engine.Context() + + // Expose the eth library and the ui library to QML + context.SetVar("eth", ui.lib) + context.SetVar("ui", &UiLib{engine: ui.engine, eth: ui.eth}) + + // Load the main QML interface + component, err := ui.engine.LoadFile(AssetPath("qml/wallet.qml")) + if err != nil { + panic(err) + } + ui.engine.LoadFile(AssetPath("qml/transactions.qml")) + + ui.win = component.CreateWindow(nil) + + // Register the ui as a block processor + //ui.eth.BlockManager.SecondaryBlockProcessor = ui + //ui.eth.TxPool.SecondaryProcessor = ui + + // Add the ui as a log system so we can log directly to the UGI + ethutil.Config.Log.AddLogSystem(ui) + + // Loads previous blocks + go ui.setInitialBlockChain() + go ui.readPreviousTransactions() + go ui.update() + + ui.win.Show() + ui.win.Wait() + + ui.eth.Stop() +} + +func (ui *Gui) setInitialBlockChain() { + // Load previous 10 blocks + chain := ui.eth.BlockChain().GetChain(ui.eth.BlockChain().CurrentBlock.Hash(), 10) + for _, block := range chain { + ui.ProcessBlock(block) + } + +} + +func (ui *Gui) readPreviousTransactions() { + it := ui.txDb.Db().NewIterator(nil, nil) + for it.Next() { + tx := ethchain.NewTransactionFromBytes(it.Value()) + + ui.win.Root().Call("addTx", NewTxFromTransaction(tx)) + } + it.Release() +} + +func (ui *Gui) ProcessBlock(block *ethchain.Block) { + ui.win.Root().Call("addBlock", NewBlockFromBlock(block)) +} + +// Simple go routine function that updates the list of peers in the GUI +func (ui *Gui) update() { + txChan := make(chan ethchain.TxMsg, 1) + ui.eth.TxPool().Subscribe(txChan) + + account := ui.eth.StateManager().GetAddrState(ui.addr).Account + unconfirmedFunds := new(big.Int) + ui.win.Root().Call("setWalletValue", fmt.Sprintf("%v", ethutil.CurrencyToString(account.Amount))) + for { + select { + case txMsg := <-txChan: + tx := txMsg.Tx + + if txMsg.Type == ethchain.TxPre { + if bytes.Compare(tx.Sender(), ui.addr) == 0 { + ui.win.Root().Call("addTx", NewTxFromTransaction(tx)) + ui.txDb.Put(tx.Hash(), tx.RlpEncode()) + + ui.eth.StateManager().GetAddrState(ui.addr).Nonce += 1 + unconfirmedFunds.Sub(unconfirmedFunds, tx.Value) + } else if bytes.Compare(tx.Recipient, ui.addr) == 0 { + ui.win.Root().Call("addTx", NewTxFromTransaction(tx)) + ui.txDb.Put(tx.Hash(), tx.RlpEncode()) + + unconfirmedFunds.Add(unconfirmedFunds, tx.Value) + } + + pos := "+" + if unconfirmedFunds.Cmp(big.NewInt(0)) >= 0 { + pos = "-" + } + val := ethutil.CurrencyToString(new(big.Int).Abs(ethutil.BigCopy(unconfirmedFunds))) + str := fmt.Sprintf("%v (%s %v)", ethutil.CurrencyToString(account.Amount), pos, val) + + ui.win.Root().Call("setWalletValue", str) + } else { + amount := account.Amount + if bytes.Compare(tx.Sender(), ui.addr) == 0 { + amount.Sub(account.Amount, tx.Value) + } else if bytes.Compare(tx.Recipient, ui.addr) == 0 { + amount.Add(account.Amount, tx.Value) + } + + ui.win.Root().Call("setWalletValue", fmt.Sprintf("%v", ethutil.CurrencyToString(amount))) + } + } + + /* + accountAmount := ui.eth.BlockManager.GetAddrState(ui.addr).Account.Amount + ui.win.Root().Call("setWalletValue", fmt.Sprintf("%v", accountAmount)) + + ui.win.Root().Call("setPeers", fmt.Sprintf("%d / %d", ui.eth.Peers().Len(), ui.eth.MaxPeers)) + + time.Sleep(1 * time.Second) + */ + + } +} + +// Logging functions that log directly to the GUI interface +func (ui *Gui) Println(v ...interface{}) { + str := strings.TrimRight(fmt.Sprintln(v...), "\n") + lines := strings.Split(str, "\n") + for _, line := range lines { + ui.win.Root().Call("addLog", line) + } +} + +func (ui *Gui) Printf(format string, v ...interface{}) { + str := strings.TrimRight(fmt.Sprintf(format, v...), "\n") + lines := strings.Split(str, "\n") + for _, line := range lines { + ui.win.Root().Call("addLog", line) + } +} diff --git a/ethereal/ui/library.go b/ethereal/ui/library.go new file mode 100644 index 000000000..05fffd579 --- /dev/null +++ b/ethereal/ui/library.go @@ -0,0 +1,60 @@ +package ethui + +import ( + "encoding/hex" + "fmt" + "github.com/ethereum/eth-go/ethchain" + "github.com/ethereum/eth-go/ethutil" + "strings" +) + +type EthLib struct { + stateManager *ethchain.StateManager + blockChain *ethchain.BlockChain + txPool *ethchain.TxPool +} + +func (lib *EthLib) CreateTx(receiver, a, data string) string { + var hash []byte + if len(receiver) == 0 { + hash = ethchain.ContractAddr + } else { + var err error + hash, err = hex.DecodeString(receiver) + if err != nil { + return err.Error() + } + } + + k, _ := ethutil.Config.Db.Get([]byte("KeyRing")) + keyPair := ethutil.NewKeyFromBytes(k) + + amount := ethutil.Big(a) + code := ethchain.Compile(strings.Split(data, "\n")) + tx := ethchain.NewTransaction(hash, amount, code) + tx.Nonce = lib.stateManager.GetAddrState(keyPair.Address()).Nonce + + tx.Sign(keyPair.PrivateKey) + + lib.txPool.QueueTransaction(tx) + + if len(receiver) == 0 { + ethutil.Config.Log.Infof("Contract addr %x", tx.Hash()[12:]) + } else { + ethutil.Config.Log.Infof("Tx hash %x", tx.Hash()) + } + + return ethutil.Hex(tx.Hash()) +} + +func (lib *EthLib) GetBlock(hexHash string) *Block { + hash, err := hex.DecodeString(hexHash) + if err != nil { + return nil + } + + block := lib.blockChain.GetBlock(hash) + fmt.Println(block) + + return &Block{Number: int(block.BlockInfo().Number), Hash: ethutil.Hex(block.Hash())} +} diff --git a/ethereal/ui/ui_lib.go b/ethereal/ui/ui_lib.go new file mode 100644 index 000000000..3997191fa --- /dev/null +++ b/ethereal/ui/ui_lib.go @@ -0,0 +1,76 @@ +package ethui + +import ( + "bitbucket.org/kardianos/osext" + "github.com/ethereum/eth-go" + "github.com/ethereum/eth-go/ethutil" + "github.com/niemeyer/qml" + "os" + "path" + "path/filepath" + "runtime" +) + +// UI Library that has some basic functionality exposed +type UiLib struct { + engine *qml.Engine + eth *eth.Ethereum + connected bool +} + +// Opens a QML file (external application) +func (ui *UiLib) Open(path string) { + component, err := ui.engine.LoadFile(path[7:]) + if err != nil { + ethutil.Config.Log.Debugln(err) + } + win := component.CreateWindow(nil) + + go func() { + win.Show() + win.Wait() + }() +} + +func (ui *UiLib) Connect(button qml.Object) { + if !ui.connected { + ui.eth.Start() + ui.connected = true + button.Set("enabled", false) + } +} + +func (ui *UiLib) ConnectToPeer(addr string) { + ui.eth.ConnectToPeer(addr) +} + +func (ui *UiLib) AssetPath(p string) string { + return AssetPath(p) +} + +func AssetPath(p string) string { + var base string + + // If the current working directory is the go-ethereum dir + // 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") { + base = path.Join(pwd, "assets") + } else { + switch runtime.GOOS { + case "darwin": + // Get Binary Directory + exedir, _ := osext.ExecutableFolder() + base = filepath.Join(exedir, "../Resources") + case "linux": + base = "/usr/share/ethereal" + case "window": + fallthrough + default: + base = "." + } + } + + return path.Join(base, p) +} -- cgit