Merge branch 'release/poc5-rc4'

This commit is contained in:
obscuren 2014-05-13 16:37:47 +02:00
commit 1adfc272a8
11 changed files with 360 additions and 190 deletions

View File

@ -36,6 +36,21 @@ window.eth = {
postData({call: "getKey"}, cb); 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) { getBalanceAt: function(address, cb) {
postData({call: "getBalance", args: [address]}, cb); postData({call: "getBalance", args: [address]}, cb);
}, },
@ -115,6 +130,8 @@ window.eth = {
} }
} }
}, },
} }
window.eth._callbacks = {} window.eth._callbacks = {}
window.eth._onCallbacks = {} window.eth._onCallbacks = {}

View File

@ -10,146 +10,146 @@ import QtQuick.Controls.Styles 1.1
import QtQuick.Dialogs 1.1 import QtQuick.Dialogs 1.1
ApplicationWindow { ApplicationWindow {
id: wizardRoot id: wizardRoot
width: 500 width: 500
height: 400 height: 400
title: "Ethereal first run setup" title: "Ethereal first run setup"
Column {
spacing: 5
anchors.leftMargin: 10
anchors.left: parent.left
Text {
visible: true
text: "<h2>Ethereal setup</h2>"
}
Column { Column {
id: restoreColumn spacing: 5
spacing: 5 anchors.leftMargin: 10
Text { anchors.left: parent.left
visible: true
font.pointSize: 14
text: "Restore your Ethereum account"
id: restoreLabel
}
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 { Text {
id: detailLabel visible: true
font.pointSize: 12 text: "<h2>Ethereal setup</h2>"
anchors.topMargin: 10
} }
}
}
Column {
id: importedDetails
visible: false
Text {
text: "<b>Your account has been imported. Please close the application and restart it again to let the changes take effect.</b>"
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: "<b>A new account has been created. Please take the time to write down the <i>24 words</i>. You can use those to restore your account at a later date.</b>"
wrapMode: Text.WordWrap
width: 480
}
Label {
text: "Please restart the application once you have completed the steps above."
wrapMode: Text.WordWrap
width: 480
}
}
} Column {
Button { id: restoreColumn
anchors.right: parent.right spacing: 5
anchors.bottom: parent.bottom Text {
anchors.rightMargin: 10 visible: true
anchors.bottomMargin: 10 font.pointSize: 14
id: newKey text: "Restore your Ethereum account"
text: "I don't have an account yet" id: restoreLabel
onClicked: { }
var res = eth.createAndSetPrivKey()
mnemonicInput.text = res[0]
addressInput.text = res[1]
privkeyInput.text = res[2]
// Hide restore TextField {
restoreColumn.visible = false 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: "<b>Your account has been imported. Please close the application and restart it again to let the changes take effect.</b>"
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: "<b>A new account has been created. Please take the time to write down the <i>24 words</i>. You can use those to restore your account at a later date.</b>"
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
}
}
} }

View File

@ -47,13 +47,37 @@ ApplicationWindow {
try { try {
switch(data.call) { 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": case "getBlockByNumber":
var block = eth.getBlock("b9b56cf6f907fbee21db0cd7cbc0e6fea2fe29503a3943e275c5e467d649cb06") var block = eth.getBlock("b9b56cf6f907fbee21db0cd7cbc0e6fea2fe29503a3943e275c5e467d649cb06")
postData(data._seed, block) postData(data._seed, block)
break break
case "getBlockByHash": case "getBlockByHash":
var block = eth.getBlock("b9b56cf6f907fbee21db0cd7cbc0e6fea2fe29503a3943e275c5e467d649cb06") var block = eth.getBlock("b9b56cf6f907fbee21db0cd7cbc0e6fea2fe29503a3943e275c5e467d649cb06")
postData(data._seed, block) postData(data._seed, block)
break break
case "transact": case "transact":
require(5) require(5)
@ -94,11 +118,14 @@ ApplicationWindow {
postData(data._seed, null) postData(data._seed, null)
break; break;
case "set": case "set":
console.log("'Set' has been depcrecated")
/*
for(var key in data.args) { for(var key in data.args) {
if(webview.hasOwnProperty(key)) { if(webview.hasOwnProperty(key)) {
window[key] = data.args[key]; window[key] = data.args[key];
} }
} }
*/
break; break;
case "getSecretToAddress": case "getSecretToAddress":
require(1) require(1)

View File

@ -0,0 +1,43 @@
<html>
<head>
<title>Utils</title>
</head>
<body onload="init();">
<label>Nonce for 2ef47100e0787b915105fd5e3f4ff6752079d5cb</label>
<p id="nonce"></p>
<label>Connected peers</label>
<p id="peers"></p>
<label>Is mining</label>
<p id="isMining"></p>
<label>Is listening</label>
<p id="isListen"></p>
<label>Coinbase</label>
<p id="coinbase"></p>
<script type="text/javascript">
function init() {
eth.getTxCountAt("2ef47100e0787b915105fd5e3f4ff6752079d5cb", function(nonce){
document.querySelector("#nonce").innerHTML = nonce;
})
eth.getPeerCount(function(peerLength){
document.querySelector("#peers").innerHTML = peerLength;
})
eth.getIsMining(function(mining){
document.querySelector("#isMining").innerHTML = mining;
})
eth.getIsListening(function(listen){
document.querySelector("#isListen").innerHTML = listen;
})
eth.getCoinBase(function(address){
document.querySelector("#coinbase").innerHTML = address;
})
}
</script>
</body>
</html>

View File

@ -7,6 +7,7 @@ import (
var StartConsole bool var StartConsole bool
var StartMining bool var StartMining bool
var StartRpc bool var StartRpc bool
var RpcPort int
var UseUPnP bool var UseUPnP bool
var OutboundPort string var OutboundPort string
var ShowGenesis bool var ShowGenesis bool
@ -28,6 +29,7 @@ func Init() {
flag.BoolVar(&UseSeed, "seed", true, "seed peers") flag.BoolVar(&UseSeed, "seed", true, "seed peers")
flag.BoolVar(&GenAddr, "genaddr", false, "create a new priv/pub key") flag.BoolVar(&GenAddr, "genaddr", false, "create a new priv/pub key")
flag.BoolVar(&ExportKey, "export", false, "export private 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(&OutboundPort, "p", "30303", "listening port")
flag.StringVar(&DataDir, "dir", ".ethereal", "ethereum data directory") flag.StringVar(&DataDir, "dir", ".ethereal", "ethereum data directory")
flag.StringVar(&ImportKey, "import", "", "imports the given private key (hex)") flag.StringVar(&ImportKey, "import", "", "imports the given private key (hex)")

View File

@ -100,8 +100,12 @@ func main() {
} }
if StartRpc { if StartRpc {
ethereum.RpcServer = ethrpc.NewJsonRpcServer(ethpub.NewPEthereum(ethereum.StateManager(), ethereum.BlockChain(), ethereum.TxPool())) ethereum.RpcServer, err = ethrpc.NewJsonRpcServer(ethpub.NewPEthereum(ethereum), RpcPort)
go ethereum.RpcServer.Start() 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) log.Printf("Starting Ethereum GUI v%s\n", ethutil.Config.Ver)
@ -110,5 +114,11 @@ func main() {
ethereum.MaxPeers = MaxPeer ethereum.MaxPeers = MaxPeer
gui := ethui.New(ethereum) gui := ethui.New(ethereum)
ethereum.Start(UseSeed)
gui.Start(AssetPath) gui.Start(AssetPath)
// Wait for shutdown
ethereum.WaitForShutdown()
} }

View File

@ -34,7 +34,7 @@ type ExtApplication struct {
func NewExtApplication(container AppContainer, lib *UiLib) *ExtApplication { func NewExtApplication(container AppContainer, lib *UiLib) *ExtApplication {
app := &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 ethutil.React, 1), make(chan ethutil.React, 1),
make(chan bool), make(chan bool),

View File

@ -24,7 +24,8 @@ type Gui struct {
eth *eth.Ethereum eth *eth.Ethereum
// The public Ethereum library // The public Ethereum library
lib *EthLib lib *EthLib
uiLib *UiLib
txDb *ethdb.LDBDatabase txDb *ethdb.LDBDatabase
@ -52,7 +53,7 @@ func New(ethereum *eth.Ethereum) *Gui {
//ethereum.StateManager().WatchAddr(addr) //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} 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 = "" }, 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") ethutil.Config.Log.Infoln("[GUI] Starting GUI")
// Create a new QML engine // Create a new QML engine
gui.engine = qml.NewEngine() gui.engine = qml.NewEngine()
@ -75,19 +76,55 @@ func (gui *Gui) Start(assetPath string) {
// Expose the eth library and the ui library to QML // Expose the eth library and the ui library to QML
context.SetVar("eth", gui) context.SetVar("eth", gui)
uiLib := NewUiLib(gui.engine, gui.eth, assetPath) gui.uiLib = NewUiLib(gui.engine, gui.eth, assetPath)
context.SetVar("ui", uiLib) context.SetVar("ui", gui.uiLib)
// Load the main QML interface // Load the main QML interface
data, _ := ethutil.Config.Db.Get([]byte("KeyRing")) data, _ := ethutil.Config.Db.Get([]byte("KeyRing"))
var err error /*
var component qml.Object var err error
firstRun := len(data) == 0 var component qml.Object
firstRun := len(data) == 0
if firstRun { if firstRun {
component, err = gui.engine.LoadFile(uiLib.AssetPath("qml/first_run.qml")) 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 { } else {
component, err = gui.engine.LoadFile(uiLib.AssetPath("qml/wallet.qml")) win, err = gui.showWallet(context)
} }
if err != nil { 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'") 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) panic(err)
} }
gui.win = component.CreateWindow(nil) win.Show()
uiLib.win = gui.win win.Wait()
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() 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() { func (gui *Gui) setInitialBlockChain() {
// Load previous 10 blocks // Load previous 10 blocks
chain := gui.eth.BlockChain().GetChain(gui.eth.BlockChain().CurrentBlock.Hash(), 10) chain := gui.eth.BlockChain().GetChain(gui.eth.BlockChain().CurrentBlock.Hash(), 10)
@ -148,7 +207,7 @@ func (gui *Gui) update() {
state := gui.eth.StateManager().TransState() state := gui.eth.StateManager().TransState()
unconfirmedFunds := new(big.Int) 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 { for {
select { select {
@ -156,7 +215,7 @@ func (gui *Gui) update() {
tx := txMsg.Tx tx := txMsg.Tx
if txMsg.Type == ethchain.TxPre { 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 { if bytes.Compare(tx.Sender(), gui.addr) == 0 && object.Nonce <= tx.Nonce {
gui.win.Root().Call("addTx", ethpub.NewPTx(tx)) gui.win.Root().Call("addTx", ethpub.NewPTx(tx))
@ -182,7 +241,7 @@ func (gui *Gui) update() {
gui.win.Root().Call("setWalletValue", str) gui.win.Root().Call("setWalletValue", str)
} else { } else {
object := state.GetStateObject(gui.addr) object := state.GetAccount(gui.addr)
if bytes.Compare(tx.Sender(), gui.addr) == 0 { if bytes.Compare(tx.Sender(), gui.addr) == 0 {
object.SubAmount(tx.Value) object.SubAmount(tx.Value)
} else if bytes.Compare(tx.Recipient, gui.addr) == 0 { } else if bytes.Compare(tx.Recipient, gui.addr) == 0 {

View File

@ -7,6 +7,7 @@ import (
var StartConsole bool var StartConsole bool
var StartMining bool var StartMining bool
var StartRpc bool var StartRpc bool
var RpcPort int
var UseUPnP bool var UseUPnP bool
var OutboundPort string var OutboundPort string
var ShowGenesis bool var ShowGenesis bool
@ -26,6 +27,7 @@ func Init() {
flag.BoolVar(&ShowGenesis, "g", false, "prints genesis header and exits") flag.BoolVar(&ShowGenesis, "g", false, "prints genesis header and exits")
//flag.BoolVar(&UseGui, "gui", true, "use the gui") //flag.BoolVar(&UseGui, "gui", true, "use the gui")
flag.BoolVar(&StartRpc, "r", false, "start rpc server") 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(&NonInteractive, "y", false, "non-interactive mode (say yes to confirmations)")
flag.BoolVar(&UseUPnP, "upnp", false, "enable UPnP support") flag.BoolVar(&UseUPnP, "upnp", false, "enable UPnP support")
flag.BoolVar(&UseSeed, "seed", true, "seed peers") flag.BoolVar(&UseSeed, "seed", true, "seed peers")

View File

@ -191,7 +191,7 @@ func (i *Console) ParseInput(input string) bool {
case "contract": case "contract":
fmt.Println("Contract editor (Ctrl-D = done)") fmt.Println("Contract editor (Ctrl-D = done)")
mainInput, initInput := mutan.PreProcess(i.Editor()) mainInput, initInput := mutan.PreParse(i.Editor())
mainScript, err := utils.Compile(mainInput) mainScript, err := utils.Compile(mainInput)
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)

View File

@ -1,6 +1,7 @@
package main package main
import ( import (
"encoding/hex"
"fmt" "fmt"
"github.com/ethereum/eth-go" "github.com/ethereum/eth-go"
"github.com/ethereum/eth-go/ethchain" "github.com/ethereum/eth-go/ethchain"
@ -122,24 +123,8 @@ func main() {
// Set the max peers // Set the max peers
ethereum.MaxPeers = MaxPeer ethereum.MaxPeers = MaxPeer
if StartConsole { // Set Mining status
err := os.Mkdir(ethutil.Config.ExecPath, os.ModePerm) ethereum.Mining = StartMining
// 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)
if StartMining { if StartMining {
logger.Infoln("Miner started") logger.Infoln("Miner started")
@ -155,7 +140,9 @@ func main() {
keyRing := ethutil.NewValueFromBytes(data) keyRing := ethutil.NewValueFromBytes(data)
addr := keyRing.Get(1).Bytes() 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() 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 // Wait for shutdown
ethereum.WaitForShutdown() ethereum.WaitForShutdown()
} }