diff --git a/ethereal/assets/ext/ethereum.js b/ethereal/assets/ext/ethereum.js index f565e58bd..d4eaf97fd 100644 --- a/ethereal/assets/ext/ethereum.js +++ b/ethereal/assets/ext/ethereum.js @@ -36,6 +36,21 @@ window.eth = { postData({call: "getKey"}, cb); }, + getTxCountAt: function(address, cb) { + postData({call: "getTxCountAt", args: [address]}, cb); + }, + getIsMining: function(cb){ + postData({call: "getIsMining"}, cb) + }, + getIsListening: function(cb){ + postData({call: "getIsListening"}, cb) + }, + getCoinBase: function(cb){ + postData({call: "getCoinBase"}, cb); + }, + getPeerCount: function(cb){ + postData({call: "getPeerCount"}, cb); + }, getBalanceAt: function(address, cb) { postData({call: "getBalance", args: [address]}, cb); }, @@ -115,6 +130,8 @@ window.eth = { } } }, + + } window.eth._callbacks = {} window.eth._onCallbacks = {} diff --git a/ethereal/assets/qml/first_run.qml b/ethereal/assets/qml/first_run.qml index 0bd3b4ce1..0b1dac4c6 100644 --- a/ethereal/assets/qml/first_run.qml +++ b/ethereal/assets/qml/first_run.qml @@ -10,146 +10,146 @@ import QtQuick.Controls.Styles 1.1 import QtQuick.Dialogs 1.1 ApplicationWindow { - id: wizardRoot - width: 500 - height: 400 - title: "Ethereal first run setup" - - Column { - spacing: 5 - anchors.leftMargin: 10 - anchors.left: parent.left - - Text { - visible: true - text: "

Ethereal setup

" - } + id: wizardRoot + width: 500 + height: 400 + title: "Ethereal first run setup" Column { - id: restoreColumn - spacing: 5 - Text { - visible: true - font.pointSize: 14 - text: "Restore your Ethereum account" - id: restoreLabel - } + spacing: 5 + anchors.leftMargin: 10 + anchors.left: parent.left - TextField { - id: txPrivKey - width: 480 - placeholderText: "Private key or mnemonic words" - focus: true - onTextChanged: { - if(this.text.length == 64){ - detailLabel.text = "Private (hex) key detected." - actionButton.enabled = true - } - else if(this.text.split(" ").length == 24){ - detailLabel.text = "Mnemonic key detected." - actionButton.enabled = true - }else{ - detailLabel.text = "" - actionButton.enabled = false - } - } - } - Row { - spacing: 10 - Button { - id: actionButton - text: "Restore" - enabled: false - onClicked: { - var success = eth.importAndSetPrivKey(txPrivKey.text) - if(success){ - importedDetails.visible = true - restoreColumn.visible = false - newKey.visible = false - wizardRoot.height = 120 - } - } - } Text { - id: detailLabel - font.pointSize: 12 - anchors.topMargin: 10 + visible: true + text: "

Ethereal setup

" } - } - } - Column { - id: importedDetails - visible: false - Text { - text: "Your account has been imported. Please close the application and restart it again to let the changes take effect." - wrapMode: Text.WordWrap - width: 460 - } - } - Column { - spacing: 5 - id: newDetailsColumn - visible: false - Text { - font.pointSize: 14 - text: "Your account details" - } - Label { - text: "Address" - } - TextField { - id: addressInput - readOnly:true - width: 480 - } - Label { - text: "Private key" - } - TextField { - id: privkeyInput - readOnly:true - width: 480 - } - Label { - text: "Mnemonic words" - } - TextField { - id: mnemonicInput - readOnly:true - width: 480 - } - Label { - text: "A new account has been created. Please take the time to write down the 24 words. You can use those to restore your account at a later date." - wrapMode: Text.WordWrap - width: 480 - } - Label { - text: "Please restart the application once you have completed the steps above." - wrapMode: Text.WordWrap - width: 480 - } - } - } - Button { - anchors.right: parent.right - anchors.bottom: parent.bottom - anchors.rightMargin: 10 - anchors.bottomMargin: 10 - id: newKey - text: "I don't have an account yet" - onClicked: { - var res = eth.createAndSetPrivKey() - mnemonicInput.text = res[0] - addressInput.text = res[1] - privkeyInput.text = res[2] + Column { + id: restoreColumn + spacing: 5 + Text { + visible: true + font.pointSize: 14 + text: "Restore your Ethereum account" + id: restoreLabel + } - // Hide restore - restoreColumn.visible = false + TextField { + id: txPrivKey + width: 480 + placeholderText: "Private key or mnemonic words" + focus: true + onTextChanged: { + if(this.text.length == 64){ + detailLabel.text = "Private (hex) key detected." + actionButton.enabled = true + } + else if(this.text.split(" ").length == 24){ + detailLabel.text = "Mnemonic key detected." + actionButton.enabled = true + }else{ + detailLabel.text = "" + actionButton.enabled = false + } + } + } + Row { + spacing: 10 + Button { + id: actionButton + text: "Restore" + enabled: false + onClicked: { + var success = lib.importAndSetPrivKey(txPrivKey.text) + if(success){ + importedDetails.visible = true + restoreColumn.visible = false + newKey.visible = false + wizardRoot.height = 120 + } + } + } + Text { + id: detailLabel + font.pointSize: 12 + anchors.topMargin: 10 + } + } + } + Column { + id: importedDetails + visible: false + Text { + text: "Your account has been imported. Please close the application and restart it again to let the changes take effect." + wrapMode: Text.WordWrap + width: 460 + } + } + Column { + spacing: 5 + id: newDetailsColumn + visible: false + Text { + font.pointSize: 14 + text: "Your account details" + } + Label { + text: "Address" + } + TextField { + id: addressInput + readOnly:true + width: 480 + } + Label { + text: "Private key" + } + TextField { + id: privkeyInput + readOnly:true + width: 480 + } + Label { + text: "Mnemonic words" + } + TextField { + id: mnemonicInput + readOnly:true + width: 480 + } + Label { + text: "A new account has been created. Please take the time to write down the 24 words. You can use those to restore your account at a later date." + wrapMode: Text.WordWrap + width: 480 + } + Label { + text: "Please restart the application once you have completed the steps above." + wrapMode: Text.WordWrap + width: 480 + } + } - // Show new details - newDetailsColumn.visible = true - newKey.visible = false } - } + Button { + anchors.right: parent.right + anchors.bottom: parent.bottom + anchors.rightMargin: 10 + anchors.bottomMargin: 10 + id: newKey + text: "I don't have an account yet" + onClicked: { + var res = lib.createAndSetPrivKey() + mnemonicInput.text = res[0] + addressInput.text = res[1] + privkeyInput.text = res[2] + + // Hide restore + restoreColumn.visible = false + + // Show new details + newDetailsColumn.visible = true + newKey.visible = false + } + } } diff --git a/ethereal/assets/qml/webapp.qml b/ethereal/assets/qml/webapp.qml index 1f3c3874a..7af006f77 100644 --- a/ethereal/assets/qml/webapp.qml +++ b/ethereal/assets/qml/webapp.qml @@ -47,13 +47,37 @@ ApplicationWindow { try { switch(data.call) { + case "getCoinBase": + postData(data._seed, eth.getCoinBase()) + + break + case "getIsListening": + postData(data._seed, eth.getIsListening()) + + break + case "getIsMining": + postData(data._seed, eth.getIsMining()) + + break + case "getPeerCount": + postData(data._seed, eth.getPeerCount()) + + break + + case "getTxCountAt": + require(1) + postData(data._seed, eth.getTxCountAt(data.args[0])) + + break case "getBlockByNumber": - var block = eth.getBlock("b9b56cf6f907fbee21db0cd7cbc0e6fea2fe29503a3943e275c5e467d649cb06") + var block = eth.getBlock("b9b56cf6f907fbee21db0cd7cbc0e6fea2fe29503a3943e275c5e467d649cb06") postData(data._seed, block) + break case "getBlockByHash": - var block = eth.getBlock("b9b56cf6f907fbee21db0cd7cbc0e6fea2fe29503a3943e275c5e467d649cb06") + var block = eth.getBlock("b9b56cf6f907fbee21db0cd7cbc0e6fea2fe29503a3943e275c5e467d649cb06") postData(data._seed, block) + break case "transact": require(5) @@ -94,11 +118,14 @@ ApplicationWindow { postData(data._seed, null) break; case "set": + console.log("'Set' has been depcrecated") + /* for(var key in data.args) { if(webview.hasOwnProperty(key)) { window[key] = data.args[key]; } } + */ break; case "getSecretToAddress": require(1) diff --git a/ethereal/assets/util/test.html b/ethereal/assets/util/test.html new file mode 100644 index 000000000..d458e6670 --- /dev/null +++ b/ethereal/assets/util/test.html @@ -0,0 +1,43 @@ + + +Utils + + + +

+ + +

+ + +

+ + +

+ + +

+ + + + + diff --git a/ethereal/config.go b/ethereal/config.go index 94f896c5f..e4bdb0a00 100644 --- a/ethereal/config.go +++ b/ethereal/config.go @@ -7,6 +7,7 @@ import ( var StartConsole bool var StartMining bool var StartRpc bool +var RpcPort int var UseUPnP bool var OutboundPort string var ShowGenesis bool @@ -28,6 +29,7 @@ func Init() { 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.IntVar(&RpcPort, "rpcport", 8080, "port to start json-rpc server on") 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)") diff --git a/ethereal/ethereum.go b/ethereal/ethereum.go index 8b8889e37..15a454bdf 100644 --- a/ethereal/ethereum.go +++ b/ethereal/ethereum.go @@ -100,8 +100,12 @@ func main() { } if StartRpc { - ethereum.RpcServer = ethrpc.NewJsonRpcServer(ethpub.NewPEthereum(ethereum.StateManager(), ethereum.BlockChain(), ethereum.TxPool())) - go ethereum.RpcServer.Start() + ethereum.RpcServer, err = ethrpc.NewJsonRpcServer(ethpub.NewPEthereum(ethereum), RpcPort) + if err != nil { + log.Println("Could not start RPC interface:", err) + } else { + go ethereum.RpcServer.Start() + } } log.Printf("Starting Ethereum GUI v%s\n", ethutil.Config.Ver) @@ -110,5 +114,11 @@ func main() { ethereum.MaxPeers = MaxPeer gui := ethui.New(ethereum) + + ethereum.Start(UseSeed) + gui.Start(AssetPath) + + // Wait for shutdown + ethereum.WaitForShutdown() } diff --git a/ethereal/ui/ext_app.go b/ethereal/ui/ext_app.go index 93db0ade1..de5f15db6 100644 --- a/ethereal/ui/ext_app.go +++ b/ethereal/ui/ext_app.go @@ -34,7 +34,7 @@ type ExtApplication struct { func NewExtApplication(container AppContainer, lib *UiLib) *ExtApplication { app := &ExtApplication{ - ethpub.NewPEthereum(lib.eth.StateManager(), lib.eth.BlockChain(), lib.eth.TxPool()), + ethpub.NewPEthereum(lib.eth), make(chan ethutil.React, 1), make(chan ethutil.React, 1), make(chan bool), diff --git a/ethereal/ui/gui.go b/ethereal/ui/gui.go index 46bfa0133..7f84272d6 100644 --- a/ethereal/ui/gui.go +++ b/ethereal/ui/gui.go @@ -24,7 +24,8 @@ type Gui struct { eth *eth.Ethereum // The public Ethereum library - lib *EthLib + lib *EthLib + uiLib *UiLib txDb *ethdb.LDBDatabase @@ -52,7 +53,7 @@ func New(ethereum *eth.Ethereum) *Gui { //ethereum.StateManager().WatchAddr(addr) } - pub := ethpub.NewPEthereum(ethereum.StateManager(), ethereum.BlockChain(), ethereum.TxPool()) + pub := ethpub.NewPEthereum(ethereum) return &Gui{eth: ethereum, lib: lib, txDb: db, addr: addr, pub: pub} } @@ -67,7 +68,7 @@ func (gui *Gui) Start(assetPath string) { Init: func(p *ethpub.PTx, obj qml.Object) { p.Value = ""; p.Hash = ""; p.Address = "" }, }}) - ethutil.Config.SetClientString(fmt.Sprintf("/Ethereal v%s", "0.5.0 RC3")) + ethutil.Config.SetClientString(fmt.Sprintf("/Ethereal v%s", "0.5.0 RC4")) ethutil.Config.Log.Infoln("[GUI] Starting GUI") // Create a new QML engine gui.engine = qml.NewEngine() @@ -75,19 +76,55 @@ func (gui *Gui) Start(assetPath string) { // Expose the eth library and the ui library to QML context.SetVar("eth", gui) - uiLib := NewUiLib(gui.engine, gui.eth, assetPath) - context.SetVar("ui", uiLib) + gui.uiLib = NewUiLib(gui.engine, gui.eth, assetPath) + context.SetVar("ui", gui.uiLib) // Load the main QML interface data, _ := ethutil.Config.Db.Get([]byte("KeyRing")) - var err error - var component qml.Object - firstRun := len(data) == 0 + /* + var err error + var component qml.Object + firstRun := len(data) == 0 - if firstRun { - component, err = gui.engine.LoadFile(uiLib.AssetPath("qml/first_run.qml")) + if firstRun { + component, err = gui.engine.LoadFile(uiLib.AssetPath("qml/first_run.qml")) + } else { + component, err = gui.engine.LoadFile(uiLib.AssetPath("qml/wallet.qml")) + } + 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) + } + + gui.win = component.CreateWindow(nil) + uiLib.win = gui.win + db := &Debugger{gui.win, make(chan bool)} + gui.lib.Db = db + uiLib.Db = db + + // Add the ui as a log system so we can log directly to the UGI + ethutil.Config.Log.AddLogSystem(gui) + + // Loads previous blocks + if firstRun == false { + go gui.setInitialBlockChain() + go gui.readPreviousTransactions() + go gui.update() + } + + gui.win.Show() + gui.win.Wait() + + gui.eth.Stop() + */ + + var win *qml.Window + var err error + if len(data) == 0 { + win, err = gui.showKeyImport(context) } else { - component, err = gui.engine.LoadFile(uiLib.AssetPath("qml/wallet.qml")) + win, err = gui.showWallet(context) } 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'") @@ -95,28 +132,50 @@ func (gui *Gui) Start(assetPath string) { panic(err) } - gui.win = component.CreateWindow(nil) - uiLib.win = gui.win - db := &Debugger{gui.win, make(chan bool)} - gui.lib.Db = db - uiLib.Db = db - - // Add the ui as a log system so we can log directly to the UGI - ethutil.Config.Log.AddLogSystem(gui) - - // Loads previous blocks - if firstRun == false { - go gui.setInitialBlockChain() - go gui.readPreviousTransactions() - go gui.update() - } - - gui.win.Show() - gui.win.Wait() + win.Show() + win.Wait() gui.eth.Stop() } +func (gui *Gui) showWallet(context *qml.Context) (*qml.Window, error) { + component, err := gui.engine.LoadFile(gui.uiLib.AssetPath("qml/wallet.qml")) + if err != nil { + return nil, err + } + + win := gui.createWindow(component) + + go gui.setInitialBlockChain() + go gui.readPreviousTransactions() + go gui.update() + + return win, nil +} + +func (gui *Gui) showKeyImport(context *qml.Context) (*qml.Window, error) { + context.SetVar("lib", gui.lib) + component, err := gui.engine.LoadFile(gui.uiLib.AssetPath("qml/first_run.qml")) + if err != nil { + return nil, err + } + + return gui.createWindow(component), nil +} + +func (gui *Gui) createWindow(comp qml.Object) *qml.Window { + win := comp.CreateWindow(nil) + + gui.win = win + gui.uiLib.win = win + + db := &Debugger{gui.win, make(chan bool)} + gui.lib.Db = db + gui.uiLib.Db = db + + return gui.win +} + func (gui *Gui) setInitialBlockChain() { // Load previous 10 blocks chain := gui.eth.BlockChain().GetChain(gui.eth.BlockChain().CurrentBlock.Hash(), 10) @@ -148,7 +207,7 @@ func (gui *Gui) update() { state := gui.eth.StateManager().TransState() unconfirmedFunds := new(big.Int) - gui.win.Root().Call("setWalletValue", fmt.Sprintf("%v", ethutil.CurrencyToString(state.GetStateObject(gui.addr).Amount))) + gui.win.Root().Call("setWalletValue", fmt.Sprintf("%v", ethutil.CurrencyToString(state.GetAccount(gui.addr).Amount))) for { select { @@ -156,7 +215,7 @@ func (gui *Gui) update() { tx := txMsg.Tx if txMsg.Type == ethchain.TxPre { - object := state.GetStateObject(gui.addr) + object := state.GetAccount(gui.addr) if bytes.Compare(tx.Sender(), gui.addr) == 0 && object.Nonce <= tx.Nonce { gui.win.Root().Call("addTx", ethpub.NewPTx(tx)) @@ -182,7 +241,7 @@ func (gui *Gui) update() { gui.win.Root().Call("setWalletValue", str) } else { - object := state.GetStateObject(gui.addr) + object := state.GetAccount(gui.addr) if bytes.Compare(tx.Sender(), gui.addr) == 0 { object.SubAmount(tx.Value) } else if bytes.Compare(tx.Recipient, gui.addr) == 0 { diff --git a/ethereum/config.go b/ethereum/config.go index 234e79f12..7ca1a9801 100644 --- a/ethereum/config.go +++ b/ethereum/config.go @@ -7,6 +7,7 @@ import ( var StartConsole bool var StartMining bool var StartRpc bool +var RpcPort int var UseUPnP bool var OutboundPort string var ShowGenesis bool @@ -26,6 +27,7 @@ func Init() { flag.BoolVar(&ShowGenesis, "g", false, "prints genesis header and exits") //flag.BoolVar(&UseGui, "gui", true, "use the gui") flag.BoolVar(&StartRpc, "r", false, "start rpc server") + flag.IntVar(&RpcPort, "rpcport", 8080, "port to start json-rpc server on") flag.BoolVar(&NonInteractive, "y", false, "non-interactive mode (say yes to confirmations)") flag.BoolVar(&UseUPnP, "upnp", false, "enable UPnP support") flag.BoolVar(&UseSeed, "seed", true, "seed peers") diff --git a/ethereum/dev_console.go b/ethereum/dev_console.go index d2be43205..9bdd58942 100644 --- a/ethereum/dev_console.go +++ b/ethereum/dev_console.go @@ -191,7 +191,7 @@ func (i *Console) ParseInput(input string) bool { case "contract": fmt.Println("Contract editor (Ctrl-D = done)") - mainInput, initInput := mutan.PreProcess(i.Editor()) + mainInput, initInput := mutan.PreParse(i.Editor()) mainScript, err := utils.Compile(mainInput) if err != nil { fmt.Println(err) diff --git a/ethereum/ethereum.go b/ethereum/ethereum.go index 2f05bf2a1..babebbb48 100644 --- a/ethereum/ethereum.go +++ b/ethereum/ethereum.go @@ -1,6 +1,7 @@ package main import ( + "encoding/hex" "fmt" "github.com/ethereum/eth-go" "github.com/ethereum/eth-go/ethchain" @@ -122,24 +123,8 @@ func main() { // Set the max peers ethereum.MaxPeers = MaxPeer - if StartConsole { - err := os.Mkdir(ethutil.Config.ExecPath, os.ModePerm) - // Error is OK if the error is ErrExist - if err != nil && !os.IsExist(err) { - log.Panic("Unable to create EXECPATH:", err) - } - - console := NewConsole(ethereum) - go console.Start() - } - if StartRpc { - ethereum.RpcServer = ethrpc.NewJsonRpcServer(ethpub.NewPEthereum(ethereum.StateManager(), ethereum.BlockChain(), ethereum.TxPool())) - go ethereum.RpcServer.Start() - } - - RegisterInterrupts(ethereum) - - ethereum.Start(UseSeed) + // Set Mining status + ethereum.Mining = StartMining if StartMining { logger.Infoln("Miner started") @@ -155,7 +140,9 @@ func main() { keyRing := ethutil.NewValueFromBytes(data) addr := keyRing.Get(1).Bytes() - miner := ethminer.NewDefaultMiner(addr, ethereum) + pair, _ := ethchain.NewKeyPairFromSec(ethutil.FromHex(hex.EncodeToString(addr))) + + miner := ethminer.NewDefaultMiner(pair.Address(), ethereum) miner.Start() }() @@ -164,6 +151,29 @@ func main() { } + if StartConsole { + err := os.Mkdir(ethutil.Config.ExecPath, os.ModePerm) + // Error is OK if the error is ErrExist + if err != nil && !os.IsExist(err) { + log.Panic("Unable to create EXECPATH:", err) + } + + console := NewConsole(ethereum) + go console.Start() + } + if StartRpc { + ethereum.RpcServer, err = ethrpc.NewJsonRpcServer(ethpub.NewPEthereum(ethereum), RpcPort) + if err != nil { + logger.Infoln("Could not start RPC interface:", err) + } else { + go ethereum.RpcServer.Start() + } + } + + RegisterInterrupts(ethereum) + + ethereum.Start(UseSeed) + // Wait for shutdown ethereum.WaitForShutdown() }