aboutsummaryrefslogtreecommitdiffstats
path: root/ethereal
diff options
context:
space:
mode:
Diffstat (limited to 'ethereal')
-rw-r--r--ethereal/assets/ethereum.js115
-rw-r--r--ethereal/assets/icon.pngbin0 -> 86700 bytes
-rw-r--r--ethereal/assets/qml/newTransaction/_new_contract.qml7
-rw-r--r--ethereal/assets/qml/newTransaction/_simple_send.qml6
-rw-r--r--ethereal/assets/qml/wallet.qml5
-rw-r--r--ethereal/assets/qml/webapp.qml130
-rw-r--r--ethereal/assets/test.html55
-rw-r--r--ethereal/ethereum.go2
-rw-r--r--ethereal/ui/gui.go17
-rw-r--r--ethereal/ui/library.go53
-rw-r--r--ethereal/ui/ui_lib.go73
11 files changed, 424 insertions, 39 deletions
diff --git a/ethereal/assets/ethereum.js b/ethereal/assets/ethereum.js
new file mode 100644
index 000000000..fefad584a
--- /dev/null
+++ b/ethereal/assets/ethereum.js
@@ -0,0 +1,115 @@
+// Helper function for generating pseudo callbacks and sending data to the QML part of the application
+function postData(data, cb) {
+ data._seed = Math.floor(Math.random() * 1000000)
+ if(cb) {
+ eth._callbacks[data._seed] = cb;
+ }
+
+ if(data.args === undefined) {
+ data.args = [];
+ }
+
+ navigator.qt.postMessage(JSON.stringify(data));
+}
+
+// Main Ethereum library
+window.eth = {
+ prototype: Object(),
+
+ // Retrieve block
+ //
+ // Either supply a number or a string. Type is determent for the lookup method
+ // string - Retrieves the block by looking up the hash
+ // number - Retrieves the block by looking up the block number
+ getBlock: function(numberOrHash, cb) {
+ var func;
+ if(typeof numberOrHash == "string") {
+ func = "getBlockByHash";
+ } else {
+ func = "getBlockByNumber";
+ }
+ postData({call: func, args: [numberOrHash]}, cb);
+ },
+
+ // Create transaction
+ //
+ // Creates a transaction with the current account
+ // If no recipient is set, the Ethereum API will see it as a contract creation
+ createTx: function(recipient, value, gas, gasPrice, data, cb) {
+ postData({call: "createTx", args: [recipient, value, gas, gasPrice, data]}, cb);
+ },
+
+ getStorage: function(address, storageAddress, cb) {
+ postData({call: "getStorage", args: [address, storageAddress]}, cb);
+ },
+
+ getKey: function(cb) {
+ postData({call: "getKey"}, cb);
+ },
+
+
+ on: function(event, cb) {
+ if(eth._onCallbacks[event] === undefined) {
+ eth._onCallbacks[event] = [];
+ }
+
+ eth._onCallbacks[event].push(cb);
+
+ return this
+ },
+ off: function(event, cb) {
+ if(eth._onCallbacks[event] !== undefined) {
+ var callbacks = eth._onCallbacks[event];
+ for(var i = 0; i < callbacks.length; i++) {
+ if(callbacks[i] === cb) {
+ delete callbacks[i];
+ }
+ }
+ }
+
+ return this
+ },
+
+ trigger: function(event, data) {
+ var callbacks = eth._onCallbacks[event];
+ if(callbacks !== undefined) {
+ for(var i = 0; i < callbacks.length; i++) {
+ callbacks[i](data);
+ }
+ }
+ },
+}
+window.eth._callbacks = {}
+window.eth._onCallbacks = {}
+
+function debug(/**/) {
+ var args = arguments;
+ var msg = ""
+ for(var i = 0; i < args.length; i++){
+ if(typeof args[i] == "object") {
+ msg += " " + JSON.stringify(args[i])
+ } else {
+ msg += args[i]
+ }
+ }
+
+ document.getElementById("debug").innerHTML += "<br>" + msg
+}
+
+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) {
+ // Call the callback
+ cb(data.data);
+ // Remove the "trigger" callback
+ delete eth._callbacks[ev._seed];
+ }
+ }
+ }
+}
diff --git a/ethereal/assets/icon.png b/ethereal/assets/icon.png
new file mode 100644
index 000000000..73e0ceb75
--- /dev/null
+++ b/ethereal/assets/icon.png
Binary files differ
diff --git a/ethereal/assets/qml/newTransaction/_new_contract.qml b/ethereal/assets/qml/newTransaction/_new_contract.qml
index abaac1695..0794d3dcd 100644
--- a/ethereal/assets/qml/newTransaction/_new_contract.qml
+++ b/ethereal/assets/qml/newTransaction/_new_contract.qml
@@ -117,6 +117,7 @@ Component {
TextArea {
id: codeView
+ height: 300
anchors.topMargin: 5
Layout.fillWidth: true
width: parent.width /2
@@ -135,18 +136,18 @@ Component {
Button {
id: txButton
+ /* enabled: false */
states: [
State {
name: "READY"
- PropertyChanges { target: txButton; enabled: true}
+ PropertyChanges { target: txButton; /*enabled: true*/}
},
State {
name: "NOTREADY"
- PropertyChanges { target: txButton; enabled:false}
+ PropertyChanges { target: txButton; /*enabled:false*/}
}
]
text: "Send"
- enabled: false
onClicked: {
//this.enabled = false
var res = eth.createTx(txFuelRecipient.text, txValue.text, txGas.text, txGasPrice.text, codeView.text)
diff --git a/ethereal/assets/qml/newTransaction/_simple_send.qml b/ethereal/assets/qml/newTransaction/_simple_send.qml
index 981766160..d460797ea 100644
--- a/ethereal/assets/qml/newTransaction/_simple_send.qml
+++ b/ethereal/assets/qml/newTransaction/_simple_send.qml
@@ -63,18 +63,18 @@ Component {
}
Button {
id: txSimpleButton
+ /*enabled: false*/
states: [
State {
name: "READY"
- PropertyChanges { target: txSimpleButton; enabled: true}
+ PropertyChanges { target: txSimpleButton; /*enabled: true*/}
},
State {
name: "NOTREADY"
- PropertyChanges { target: txSimpleButton; enabled: false}
+ PropertyChanges { target: txSimpleButton; /*enabled: false*/}
}
]
text: "Send"
- enabled: false
onClicked: {
//this.enabled = false
var res = eth.createTx(txSimpleRecipient.text, txSimpleValue.text,"","","")
diff --git a/ethereal/assets/qml/wallet.qml b/ethereal/assets/qml/wallet.qml
index 37224c7b4..574fbef86 100644
--- a/ethereal/assets/qml/wallet.qml
+++ b/ethereal/assets/qml/wallet.qml
@@ -240,7 +240,10 @@ ApplicationWindow {
id: openAppDialog
title: "Open QML Application"
onAccepted: {
- ui.open(openAppDialog.fileUrl.toString())
+ //ui.open(openAppDialog.fileUrl.toString())
+ //ui.openHtml(Qt.resolvedUrl(ui.assetPath("test.html")))
+ ui.openHtml(openAppDialog.fileUrl.toString())
+
}
}
diff --git a/ethereal/assets/qml/webapp.qml b/ethereal/assets/qml/webapp.qml
new file mode 100644
index 000000000..9cf154e9b
--- /dev/null
+++ b/ethereal/assets/qml/webapp.qml
@@ -0,0 +1,130 @@
+import QtQuick 2.0
+import QtWebKit 3.0
+import QtWebKit.experimental 1.0
+import QtQuick.Controls 1.0;
+import QtQuick.Layouts 1.0;
+import QtQuick.Window 2.1;
+import Ethereum 1.0
+
+ApplicationWindow {
+ id: window
+ title: "Ethereum"
+ width: 900
+ height: 600
+ minimumHeight: 300
+
+ property alias url: webview.url
+ property alias webView: webview
+
+ Item {
+ objectName: "root"
+ id: root
+ anchors.fill: parent
+ state: "inspectorShown"
+
+ WebView {
+ objectName: "webView"
+ id: webview
+ anchors.fill: parent
+ /*
+ anchors {
+ left: parent.left
+ right: parent.right
+ bottom: sizeGrip.top
+ top: parent.top
+ }
+ */
+
+ onTitleChanged: { window.title = title }
+ experimental.preferences.javascriptEnabled: true
+ experimental.preferences.navigatorQtObjectEnabled: true
+ experimental.preferences.developerExtrasEnabled: true
+ experimental.userScripts: [ui.assetPath("ethereum.js")]
+ experimental.onMessageReceived: {
+ //console.log("[onMessageReceived]: ", message.data)
+ var data = JSON.parse(message.data)
+
+ switch(data.call) {
+ case "getBlockByNumber":
+ var block = eth.getBlock("b9b56cf6f907fbee21db0cd7cbc0e6fea2fe29503a3943e275c5e467d649cb06")
+ postData(data._seed, block)
+ break
+ case "getBlockByHash":
+ var block = eth.getBlock("b9b56cf6f907fbee21db0cd7cbc0e6fea2fe29503a3943e275c5e467d649cb06")
+ postData(data._seed, block)
+ break
+ case "createTx":
+ if(data.args.length < 5) {
+ postData(data._seed, null)
+ } else {
+ var tx = eth.createTx(data.args[0], data.args[1],data.args[2],data.args[3],data.args[4])
+ postData(data._seed, tx)
+ }
+ break
+ case "getStorage":
+ if(data.args.length < 2) {
+ postData(data._seed, null)
+ } else {
+ var stateObject = eth.getStateObject(data.args[0])
+ var storage = stateObject.getStorage(data.args[1])
+ postData(data._seed, storage)
+ }
+ break
+ case "getKey":
+ var keys = eth.getKey()
+ postData(data._seed, keys)
+ break
+ }
+ }
+ function postData(seed, data) {
+ webview.experimental.postMessage(JSON.stringify({data: data, _seed: seed}))
+ }
+
+ function onNewBlockCb(block) {
+ webview.experimental.postMessage(JSON.stringify({data: block, _event: "block:new"}))
+ }
+ }
+
+ Rectangle {
+ id: sizeGrip
+ color: "gray"
+ visible: false
+ height: 10
+ anchors {
+ left: root.left
+ right: root.right
+ }
+ y: Math.round(root.height * 2 / 3)
+
+ MouseArea {
+ anchors.fill: parent
+ drag.target: sizeGrip
+ drag.minimumY: 0
+ drag.maximumY: root.height
+ drag.axis: Drag.YAxis
+ }
+ }
+
+ WebView {
+ id: inspector
+ visible: false
+ url: webview.experimental.remoteInspectorUrl
+ anchors {
+ left: root.left
+ right: root.right
+ top: sizeGrip.bottom
+ bottom: root.bottom
+ }
+ }
+
+ states: [
+ State {
+ name: "inspectorShown"
+ PropertyChanges {
+ target: inspector
+ url: webview.experimental.remoteInspectorUrl
+ }
+ }
+ ]
+ }
+}
diff --git a/ethereal/assets/test.html b/ethereal/assets/test.html
new file mode 100644
index 000000000..beb888685
--- /dev/null
+++ b/ethereal/assets/test.html
@@ -0,0 +1,55 @@
+<html>
+<head>
+<title>jeffcoin</title>
+<script type="text/javascript">
+var jefcoinAddr = "3dff537f51350239abc95c76a5864aa605259e7d"
+
+function createTransaction() {
+ var addr = document.querySelector("#addr").value;
+ var amount = document.querySelector("#amount").value;
+
+ var data = "0x" + addr + "\n" + amount
+ eth.createTx(jefcoinAddr, 0, "10000000", "250", data, function(tx) {
+ debug("received tx hash:", tx)
+ })
+}
+
+function init() {
+ eth.getKey(function(key) {
+ eth.getStorage(jefcoinAddr, key, function(storage) {
+ document.querySelector("#currentAmount").innerHTML = "Amount: " + storage;
+ });
+
+ eth.on("block:new", function() {
+ eth.getStorage(jefcoinAddr, key, function(storage) {
+ document.querySelector("#currentAmount").innerHTML = "Amount: " + storage;
+ });
+ });
+ });
+}
+
+</script>
+<style type="text/css">
+input[type="text"] {
+ width: 300px;
+}
+</style>
+</head>
+
+<body onload="init();">
+<h1>Jeff Coin</h1>
+
+<img src="icon.png">
+<div id="currentAmount"></div>
+
+<div id="transactions">
+ <input id="addr" type="text" placeholder="Receiver address"></input><br>
+ <input id="amount" type="text" placeholder="Amount"></input><br>
+ <button onclick="createTransaction();">Send Tx</button>
+</div>
+
+<div id="debug" style="border: 1px solid block"></div>
+
+</body>
+</html>
+
diff --git a/ethereal/ethereum.go b/ethereal/ethereum.go
index 9cc52039d..0adb9f151 100644
--- a/ethereal/ethereum.go
+++ b/ethereal/ethereum.go
@@ -7,7 +7,7 @@ import (
"github.com/ethereum/eth-go/ethutil"
"github.com/ethereum/go-ethereum/ethereal/ui"
"github.com/ethereum/go-ethereum/utils"
- "github.com/niemeyer/qml"
+ "github.com/go-qml/qml"
"log"
"os"
"os/signal"
diff --git a/ethereal/ui/gui.go b/ethereal/ui/gui.go
index 1065b716e..80498d718 100644
--- a/ethereal/ui/gui.go
+++ b/ethereal/ui/gui.go
@@ -8,7 +8,7 @@ import (
"github.com/ethereum/eth-go/ethchain"
"github.com/ethereum/eth-go/ethdb"
"github.com/ethereum/eth-go/ethutil"
- "github.com/niemeyer/qml"
+ "github.com/go-qml/qml"
"math/big"
"strings"
)
@@ -24,6 +24,18 @@ type Tx struct {
Contract bool
}
+type Key struct {
+ Address string
+}
+
+type KeyRing struct {
+ Keys []interface{}
+}
+
+func NewKeyRing(keys []interface{}) *KeyRing {
+ return &KeyRing{Keys: keys}
+}
+
func NewTxFromTransaction(tx *ethchain.Transaction) *Tx {
hash := hex.EncodeToString(tx.Hash())
sender := hex.EncodeToString(tx.Recipient)
@@ -113,6 +125,7 @@ func (ui *Gui) Start(assetPath string) {
}
if err != nil {
ethutil.Config.Log.Infoln("FATAL: asset not found: you can set an alternative asset path on on the command line using option 'asset_path'")
+
panic(err)
}
@@ -170,7 +183,7 @@ func (ui *Gui) update() {
txChan := make(chan ethchain.TxMsg, 1)
ui.eth.TxPool().Subscribe(txChan)
- account := ui.eth.StateManager().GetAddrState(ui.addr).Account
+ account := ui.eth.StateManager().GetAddrState(ui.addr).Object
unconfirmedFunds := new(big.Int)
ui.win.Root().Call("setWalletValue", fmt.Sprintf("%v", ethutil.CurrencyToString(account.Amount)))
for {
diff --git a/ethereal/ui/library.go b/ethereal/ui/library.go
index 42aebcd87..5ca2b4273 100644
--- a/ethereal/ui/library.go
+++ b/ethereal/ui/library.go
@@ -6,11 +6,24 @@ import (
"github.com/ethereum/eth-go/ethchain"
"github.com/ethereum/eth-go/ethutil"
"github.com/ethereum/go-ethereum/utils"
- "github.com/obscuren/mutan"
"github.com/obscuren/secp256k1-go"
"strings"
)
+type Contract struct {
+ object *ethchain.StateObject
+}
+
+func NewContract(object *ethchain.StateObject) *Contract {
+ return &Contract{object: object}
+}
+
+func (c *Contract) GetStorage(address string) string {
+ val := c.object.GetMem(ethutil.Big("0x" + address))
+
+ return val.BigInt().String()
+}
+
type EthLib struct {
stateManager *ethchain.StateManager
blockChain *ethchain.BlockChain
@@ -44,7 +57,17 @@ func (lib *EthLib) CreateAndSetPrivKey() (string, string, string, string) {
return mnemonicString, fmt.Sprintf("%x", pair.Address()), fmt.Sprintf("%x", prv), fmt.Sprintf("%x", pub)
}
-func (lib *EthLib) CreateTx(recipient, valueStr, gasStr, gasPriceStr, data string) (string, error) {
+func (lib *EthLib) GetKey() string {
+ return ethutil.Hex(ethutil.Config.Db.GetKeys()[0].Address())
+}
+
+func (lib *EthLib) GetStateObject(address string) *Contract {
+ stateObject := lib.stateManager.ProcState().GetContract(ethutil.FromHex(address))
+
+ return NewContract(stateObject)
+}
+
+func (lib *EthLib) CreateTx(recipient, valueStr, gasStr, gasPriceStr, dataStr string) (string, error) {
var hash []byte
var contractCreation bool
if len(recipient) == 0 {
@@ -64,25 +87,24 @@ func (lib *EthLib) CreateTx(recipient, valueStr, gasStr, gasPriceStr, data strin
var tx *ethchain.Transaction
// Compile and assemble the given data
if contractCreation {
- asm, errors := mutan.Compile(strings.NewReader(data), false)
- if len(errors) > 0 {
- var errs string
- for _, er := range errors {
- if er != nil {
- errs += er.Error()
- }
- }
- return "", fmt.Errorf(errs)
+ // Compile script
+ mainScript, initScript, err := utils.CompileScript(dataStr)
+ if err != nil {
+ return "", err
}
- code := ethutil.Assemble(asm...)
- tx = ethchain.NewContractCreationTx(value, gasPrice, code)
+ tx = ethchain.NewContractCreationTx(value, gas, gasPrice, mainScript, initScript)
} else {
- tx = ethchain.NewTransactionMessage(hash, value, gasPrice, gas, nil)
+ lines := strings.Split(dataStr, "\n")
+ var data []byte
+ for _, line := range lines {
+ data = append(data, ethutil.BigToBytes(ethutil.Big(line), 256)...)
+ }
+
+ tx = ethchain.NewTransactionMessage(hash, value, gas, gasPrice, data)
}
acc := lib.stateManager.GetAddrState(keyPair.Address())
tx.Nonce = acc.Nonce
- //acc.Nonce++
tx.Sign(keyPair.PrivateKey)
lib.txPool.QueueTransaction(tx)
@@ -102,7 +124,6 @@ func (lib *EthLib) GetBlock(hexHash string) *Block {
}
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
index b2552cdce..08e2267a7 100644
--- a/ethereal/ui/ui_lib.go
+++ b/ethereal/ui/ui_lib.go
@@ -6,14 +6,12 @@ import (
"github.com/ethereum/eth-go"
"github.com/ethereum/eth-go/ethchain"
"github.com/ethereum/eth-go/ethutil"
- "github.com/niemeyer/qml"
- "github.com/obscuren/mutan"
- "math/big"
+ "github.com/ethereum/go-ethereum/utils"
+ "github.com/go-qml/qml"
"os"
"path"
"path/filepath"
"runtime"
- "strings"
)
type memAddr struct {
@@ -53,6 +51,56 @@ 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)
+
+ return
+ }
+ win := component.CreateWindow(nil)
+ if filepath.Ext(path) == "eth" {
+ fmt.Println("Ethereum package not yet supported")
+
+ return
+
+ // TODO
+ ethutil.OpenPackage(path)
+ }
+ win.Set("url", path)
+
+ go func() {
+ blockChan := 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 := &Block{Number: int(block.BlockInfo().Number), Hash: ethutil.Hex(block.Hash())}
+ win.ObjectByName("webView").Call("onNewBlockCb", b)
+ }
+ }
+ }
+
+ // Clean up
+ close(blockChan)
+ close(quitChan)
+ }()
+ ui.eth.Reactor().Subscribe("newBlock", blockChan)
+
+ win.Show()
+ win.Wait()
+
+ quitChan <- true
+ }()
+}
+
func (ui *UiLib) Connect(button qml.Object) {
if !ui.connected {
ui.eth.Start()
@@ -99,27 +147,26 @@ func (ui *UiLib) DebugTx(recipient, valueStr, gasStr, gasPriceStr, data string)
state := ui.eth.BlockChain().CurrentBlock.State()
mainInput, _ := ethutil.PreProcess(data)
- asm, err := mutan.Compile(strings.NewReader(mainInput), false)
+ callerScript, err := utils.Compile(mainInput)
if err != nil {
- fmt.Println(err)
- for _, e := range err {
- ui.win.Root().Call("addDebugMessage", e.Error())
- }
+ ethutil.Config.Log.Debugln(err)
+
+ return
}
- callerScript := ethutil.Assemble(asm...)
dis := ethchain.Disassemble(callerScript)
ui.win.Root().Call("clearAsm")
+
for _, str := range dis {
ui.win.Root().Call("setAsm", str)
}
- callerTx := ethchain.NewContractCreationTx(ethutil.Big(valueStr), ethutil.Big(gasPriceStr), callerScript)
+ callerTx := ethchain.NewContractCreationTx(ethutil.Big(valueStr), ethutil.Big(gasStr), ethutil.Big(gasPriceStr), callerScript, nil)
// Contract addr as test address
keyPair := ethutil.Config.Db.GetKeys()[0]
- account := ui.eth.StateManager().GetAddrState(keyPair.Address()).Account
+ account := ui.eth.StateManager().GetAddrState(keyPair.Address()).Object
c := ethchain.MakeContract(callerTx, state)
- callerClosure := ethchain.NewClosure(account, c, c.Script(), state, ethutil.Big(gasStr), new(big.Int))
+ callerClosure := ethchain.NewClosure(account, c, c.Script(), state, ethutil.Big(gasStr), ethutil.Big(gasPriceStr), ethutil.Big(valueStr))
block := ui.eth.BlockChain().CurrentBlock
vm := ethchain.NewVm(state, ethchain.RuntimeVars{