Implemented new miner w/ ui interface for merged mining. Closes #177
* Miner has been rewritten * Added new miner pane * Added option for local txs * Added option to read from MergeMining contract and list them for merged mining
This commit is contained in:
BIN
cmd/mist/assets/miner.png
Normal file
BIN
cmd/mist/assets/miner.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.1 KiB |
@ -12,7 +12,6 @@ import "../ext/http.js" as Http
|
||||
ApplicationWindow {
|
||||
id: root
|
||||
|
||||
property alias miningButtonText: miningButton.text
|
||||
property var ethx : Eth.ethx
|
||||
property var browser
|
||||
|
||||
@ -47,6 +46,7 @@ ApplicationWindow {
|
||||
Component.onCompleted: {
|
||||
var wallet = addPlugin("./views/wallet.qml", {noAdd: true, close: false, section: "ethereum", active: true});
|
||||
var browser = addPlugin("./webapp.qml", {noAdd: true, close: false, section: "ethereum", active: true});
|
||||
var browser = addPlugin("./views/miner.qml", {noAdd: true, close: false, section: "ethereum", active: true});
|
||||
root.browser = browser;
|
||||
|
||||
addPlugin("./views/transaction.qml", {noAdd: true, close: false, section: "legacy"});
|
||||
@ -252,29 +252,18 @@ ApplicationWindow {
|
||||
}
|
||||
|
||||
statusBar: StatusBar {
|
||||
height: 32
|
||||
//height: 32
|
||||
id: statusBar
|
||||
RowLayout {
|
||||
Button {
|
||||
id: miningButton
|
||||
text: "Start Mining"
|
||||
onClicked: {
|
||||
gui.toggleMining()
|
||||
}
|
||||
}
|
||||
Label {
|
||||
//y: 6
|
||||
id: walletValueLabel
|
||||
|
||||
RowLayout {
|
||||
Label {
|
||||
id: walletValueLabel
|
||||
|
||||
font.pixelSize: 10
|
||||
styleColor: "#797979"
|
||||
}
|
||||
}
|
||||
font.pixelSize: 10
|
||||
styleColor: "#797979"
|
||||
}
|
||||
|
||||
Label {
|
||||
y: 6
|
||||
//y: 6
|
||||
objectName: "miningLabel"
|
||||
visible: true
|
||||
font.pixelSize: 10
|
||||
@ -283,7 +272,7 @@ ApplicationWindow {
|
||||
}
|
||||
|
||||
Label {
|
||||
y: 6
|
||||
//y: 6
|
||||
id: lastBlockLabel
|
||||
objectName: "lastBlockLabel"
|
||||
visible: true
|
||||
@ -297,14 +286,14 @@ ApplicationWindow {
|
||||
id: downloadIndicator
|
||||
value: 0
|
||||
objectName: "downloadIndicator"
|
||||
y: 3
|
||||
y: -4
|
||||
x: statusBar.width / 2 - this.width / 2
|
||||
width: 160
|
||||
}
|
||||
|
||||
Label {
|
||||
objectName: "downloadLabel"
|
||||
y: 7
|
||||
//y: 7
|
||||
anchors.left: downloadIndicator.right
|
||||
anchors.leftMargin: 5
|
||||
font.pixelSize: 10
|
||||
@ -314,7 +303,7 @@ ApplicationWindow {
|
||||
|
||||
RowLayout {
|
||||
id: peerGroup
|
||||
y: 7
|
||||
//y: 7
|
||||
anchors.right: parent.right
|
||||
MouseArea {
|
||||
onDoubleClicked: peerWindow.visible = true
|
||||
@ -323,14 +312,9 @@ ApplicationWindow {
|
||||
|
||||
Label {
|
||||
id: peerLabel
|
||||
font.pixelSize: 8
|
||||
font.pixelSize: 10
|
||||
text: "0 / 0"
|
||||
}
|
||||
Image {
|
||||
id: peerImage
|
||||
width: 10; height: 10
|
||||
source: "../network.png"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
254
cmd/mist/assets/qml/views/miner.qml
Normal file
254
cmd/mist/assets/qml/views/miner.qml
Normal file
@ -0,0 +1,254 @@
|
||||
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
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
property var title: "Miner"
|
||||
property var iconSource: "../miner.png"
|
||||
property var menuItem
|
||||
|
||||
color: "#00000000"
|
||||
|
||||
ColumnLayout {
|
||||
spacing: 10
|
||||
anchors.fill: parent
|
||||
|
||||
Rectangle {
|
||||
id: mainPane
|
||||
color: "#00000000"
|
||||
anchors {
|
||||
top: parent.top
|
||||
bottom: localTxPane.top
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: menu
|
||||
height: 25
|
||||
anchors {
|
||||
left: parent.left
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: tools
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
}
|
||||
|
||||
Button {
|
||||
text: "Start"
|
||||
onClicked: {
|
||||
eth.setGasPrice(minGasPrice.text || "10000000000000");
|
||||
if (eth.toggleMining()) {
|
||||
this.text = "Stop";
|
||||
} else {
|
||||
this.text = "Start";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 2
|
||||
width: 200
|
||||
TextField {
|
||||
id: minGasPrice
|
||||
placeholderText: "Min Gas: 10000000000000"
|
||||
width: 200
|
||||
validator: RegExpValidator { regExp: /\d*/ }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
top: menu.bottom
|
||||
topMargin: 5
|
||||
}
|
||||
|
||||
Text {
|
||||
text: "<b>Merged mining options</b>"
|
||||
}
|
||||
|
||||
TableView {
|
||||
id: mergedMiningTable
|
||||
height: 300
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
}
|
||||
Component {
|
||||
id: checkBoxDelegate
|
||||
|
||||
Item {
|
||||
id: test
|
||||
CheckBox {
|
||||
anchors.fill: parent
|
||||
checked: styleData.value
|
||||
|
||||
onClicked: {
|
||||
var model = mergedMiningModel.get(styleData.row)
|
||||
|
||||
if (this.checked) {
|
||||
model.id = txModel.createLocalTx(model.address, "0", "5000", "0", "")
|
||||
} else {
|
||||
txModel.removeWithId(model.id);
|
||||
model.id = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
TableViewColumn{ role: "checked" ; title: "" ; width: 40 ; delegate: checkBoxDelegate }
|
||||
TableViewColumn{ role: "name" ; title: "Name" ; width: 480 }
|
||||
model: ListModel {
|
||||
objectName: "mergedMiningModel"
|
||||
id: mergedMiningModel
|
||||
function addMergedMiningOption(model) {
|
||||
this.append(model);
|
||||
}
|
||||
}
|
||||
Component.onCompleted: {
|
||||
/* interface test stuff
|
||||
// XXX Temp. replace with above eventually
|
||||
var tmpItems = ["JEVCoin", "Some coin", "Other coin", "Etc coin"];
|
||||
var address = "e6716f9544a56c530d868e4bfbacb172315bdead";
|
||||
for (var i = 0; i < tmpItems.length; i++) {
|
||||
mergedMiningModel.append({checked: false, name: tmpItems[i], address: address, id: 0, itemId: i});
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: localTxPane
|
||||
color: "#ececec"
|
||||
border.color: "#cccccc"
|
||||
border.width: 1
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
bottom: parent.bottom
|
||||
}
|
||||
height: 300
|
||||
|
||||
ColumnLayout {
|
||||
spacing: 10
|
||||
anchors.fill: parent
|
||||
RowLayout {
|
||||
id: newLocalTx
|
||||
anchors {
|
||||
left: parent.left
|
||||
leftMargin: 5
|
||||
top: parent.top
|
||||
topMargin: 5
|
||||
bottomMargin: 5
|
||||
}
|
||||
|
||||
Text {
|
||||
text: "Local tx"
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: 250
|
||||
color: "#00000000"
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 2
|
||||
|
||||
TextField {
|
||||
id: to
|
||||
placeholderText: "To"
|
||||
width: 250
|
||||
validator: RegExpValidator { regExp: /[abcdefABCDEF1234567890]*/ }
|
||||
}
|
||||
}
|
||||
TextField {
|
||||
property var defaultGas: "5000"
|
||||
id: gas
|
||||
placeholderText: "Gas"
|
||||
text: defaultGas
|
||||
validator: RegExpValidator { regExp: /\d*/ }
|
||||
}
|
||||
TextField {
|
||||
id: gasPrice
|
||||
placeholderText: "Price"
|
||||
validator: RegExpValidator { regExp: /\d*/ }
|
||||
}
|
||||
TextField {
|
||||
id: value
|
||||
placeholderText: "Amount"
|
||||
text: "0"
|
||||
validator: RegExpValidator { regExp: /\d*/ }
|
||||
}
|
||||
TextField {
|
||||
id: data
|
||||
placeholderText: "Data"
|
||||
validator: RegExpValidator { regExp: /[abcdefABCDEF1234567890]*/ }
|
||||
}
|
||||
Button {
|
||||
text: "Create"
|
||||
onClicked: {
|
||||
if (to.text.length == 40 && gasPrice.text.length != 0 && value.text.length != 0 && gas.text.length != 0) {
|
||||
txModel.createLocalTx(to.text, gasPrice.text, gas.text, value.text, data.text);
|
||||
|
||||
to.text = ""; gasPrice.text = "";
|
||||
gas.text = gas.defaultGas;
|
||||
value.text = "0"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TableView {
|
||||
id: txTableView
|
||||
anchors {
|
||||
top: newLocalTx.bottom
|
||||
topMargin: 5
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
bottom: parent.bottom
|
||||
}
|
||||
TableViewColumn{ role: "to" ; title: "To" ; width: 480 }
|
||||
TableViewColumn{ role: "gas" ; title: "Gas" ; width: 100 }
|
||||
TableViewColumn{ role: "gasPrice" ; title: "Gas Price" ; width: 100 }
|
||||
TableViewColumn{ role: "value" ; title: "Amount" ; width: 100 }
|
||||
TableViewColumn{ role: "data" ; title: "Data" ; width: 100 }
|
||||
|
||||
model: ListModel {
|
||||
id: txModel
|
||||
Component.onCompleted: {
|
||||
}
|
||||
function removeWithId(id) {
|
||||
for (var i = 0; i < this.count; i++) {
|
||||
if (txModel.get(i).id == id) {
|
||||
this.remove(i);
|
||||
eth.removeLocalTransaction(id)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function createLocalTx(to, gasPrice, gas, value, data) {
|
||||
var id = eth.addLocalTransaction(to, data, gas, gasPrice, value)
|
||||
txModel.insert(0, {to: to, gas: gas, gasPrice: gasPrice, value: value, data: data, id: id});
|
||||
|
||||
return id
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -70,10 +70,6 @@ func (gui *Gui) GetCustomIdentifier() string {
|
||||
return gui.clientIdentity.GetCustomIdentifier()
|
||||
}
|
||||
|
||||
func (gui *Gui) ToggleTurboMining() {
|
||||
gui.miner.ToggleTurbo()
|
||||
}
|
||||
|
||||
// functions that allow Gui to implement interface guilogger.LogSystem
|
||||
func (gui *Gui) SetLogLevel(level logger.LogLevel) {
|
||||
gui.logLevel = level
|
||||
@ -137,20 +133,3 @@ func (self *Gui) DumpState(hash, path string) {
|
||||
|
||||
file.Write(stateDump)
|
||||
}
|
||||
func (gui *Gui) ToggleMining() {
|
||||
var txt string
|
||||
if gui.eth.Mining {
|
||||
utils.StopMining(gui.eth)
|
||||
txt = "Start mining"
|
||||
|
||||
gui.getObjectByName("miningLabel").Set("visible", false)
|
||||
} else {
|
||||
utils.StartMining(gui.eth)
|
||||
gui.miner = utils.GetMiner()
|
||||
txt = "Stop mining"
|
||||
|
||||
gui.getObjectByName("miningLabel").Set("visible", true)
|
||||
}
|
||||
|
||||
gui.win.Root().Set("miningButtonText", txt)
|
||||
}
|
||||
|
@ -272,8 +272,6 @@ type address struct {
|
||||
|
||||
func (gui *Gui) loadAddressBook() {
|
||||
view := gui.getObjectByName("infoView")
|
||||
view.Call("clearAddress")
|
||||
|
||||
nameReg := gui.pipe.World().Config().Get("NameReg")
|
||||
if nameReg != nil {
|
||||
nameReg.EachStorage(func(name string, value *ethutil.Value) {
|
||||
@ -286,6 +284,28 @@ func (gui *Gui) loadAddressBook() {
|
||||
}
|
||||
}
|
||||
|
||||
func (self *Gui) loadMergedMiningOptions() {
|
||||
view := self.getObjectByName("mergedMiningModel")
|
||||
|
||||
nameReg := self.pipe.World().Config().Get("MergeMining")
|
||||
if nameReg != nil {
|
||||
i := 0
|
||||
nameReg.EachStorage(func(name string, value *ethutil.Value) {
|
||||
if name[0] != 0 {
|
||||
value.Decode()
|
||||
|
||||
view.Call("addMergedMiningOption", struct {
|
||||
Checked bool
|
||||
Name, Address string
|
||||
Id, ItemId int
|
||||
}{false, name, ethutil.Bytes2Hex(value.Bytes()), 0, i})
|
||||
|
||||
i++
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (gui *Gui) insertTransaction(window string, tx *chain.Transaction) {
|
||||
pipe := xeth.New(gui.eth)
|
||||
nameReg := pipe.World().Config().Get("NameReg")
|
||||
@ -382,6 +402,7 @@ func (gui *Gui) update() {
|
||||
go func() {
|
||||
go gui.setInitialChainManager()
|
||||
gui.loadAddressBook()
|
||||
gui.loadMergedMiningOptions()
|
||||
gui.setPeerInfo()
|
||||
gui.readPreviousTransactions()
|
||||
}()
|
||||
@ -410,7 +431,6 @@ func (gui *Gui) update() {
|
||||
chain.NewBlockEvent{},
|
||||
chain.TxPreEvent{},
|
||||
chain.TxPostEvent{},
|
||||
miner.Event{},
|
||||
)
|
||||
|
||||
// nameReg := gui.pipe.World().Config().Get("NameReg")
|
||||
@ -469,12 +489,14 @@ func (gui *Gui) update() {
|
||||
case eth.PeerListEvent:
|
||||
gui.setPeerInfo()
|
||||
|
||||
case miner.Event:
|
||||
if ev.Type == miner.Started {
|
||||
gui.miner = ev.Miner
|
||||
} else {
|
||||
gui.miner = nil
|
||||
}
|
||||
/*
|
||||
case miner.Event:
|
||||
if ev.Type == miner.Started {
|
||||
gui.miner = ev.Miner
|
||||
} else {
|
||||
gui.miner = nil
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
case <-peerUpdateTicker.C:
|
||||
@ -483,10 +505,13 @@ func (gui *Gui) update() {
|
||||
statusText := "#" + gui.eth.ChainManager().CurrentBlock.Number.String()
|
||||
lastBlockLabel.Set("text", statusText)
|
||||
|
||||
if gui.miner != nil {
|
||||
pow := gui.miner.GetPow()
|
||||
miningLabel.Set("text", "Mining @ "+strconv.FormatInt(pow.GetHashrate(), 10)+"Khash")
|
||||
}
|
||||
miningLabel.Set("text", "Mining @ "+strconv.FormatInt(gui.uiLib.miner.GetPow().GetHashrate(), 10)+"Khash")
|
||||
/*
|
||||
if gui.miner != nil {
|
||||
pow := gui.miner.GetPow()
|
||||
miningLabel.Set("text", "Mining @ "+strconv.FormatInt(pow.GetHashrate(), 10)+"Khash")
|
||||
}
|
||||
*/
|
||||
|
||||
blockLength := gui.eth.BlockPool().BlocksProcessed
|
||||
chainLength := gui.eth.BlockPool().ChainLength
|
||||
|
@ -29,6 +29,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/ethutil"
|
||||
"github.com/ethereum/go-ethereum/javascript"
|
||||
"github.com/ethereum/go-ethereum/miner"
|
||||
"github.com/ethereum/go-ethereum/state"
|
||||
"github.com/ethereum/go-ethereum/ui/qt"
|
||||
"github.com/ethereum/go-ethereum/xeth"
|
||||
@ -55,10 +56,15 @@ type UiLib struct {
|
||||
jsEngine *javascript.JSRE
|
||||
|
||||
filterCallbacks map[int][]int
|
||||
|
||||
miner *miner.Miner
|
||||
}
|
||||
|
||||
func NewUiLib(engine *qml.Engine, eth *eth.Ethereum, assetPath string) *UiLib {
|
||||
return &UiLib{JSXEth: xeth.NewJSXEth(eth), engine: engine, eth: eth, assetPath: assetPath, jsEngine: javascript.NewJSRE(eth), filterCallbacks: make(map[int][]int)} //, filters: make(map[int]*xeth.JSFilter)}
|
||||
lib := &UiLib{JSXEth: xeth.NewJSXEth(eth), engine: engine, eth: eth, assetPath: assetPath, jsEngine: javascript.NewJSRE(eth), filterCallbacks: make(map[int][]int)} //, filters: make(map[int]*xeth.JSFilter)}
|
||||
lib.miner = miner.New(eth.KeyManager().Address(), eth)
|
||||
|
||||
return lib
|
||||
}
|
||||
|
||||
func (self *UiLib) Notef(args []interface{}) {
|
||||
@ -328,3 +334,33 @@ func (self *UiLib) Call(params map[string]interface{}) (string, error) {
|
||||
object["data"],
|
||||
)
|
||||
}
|
||||
|
||||
func (self *UiLib) AddLocalTransaction(to, data, gas, gasPrice, value string) int {
|
||||
return self.miner.AddLocalTx(&miner.LocalTx{
|
||||
To: ethutil.Hex2Bytes(to),
|
||||
Data: ethutil.Hex2Bytes(data),
|
||||
Gas: gas,
|
||||
GasPrice: gasPrice,
|
||||
Value: value,
|
||||
}) - 1
|
||||
}
|
||||
|
||||
func (self *UiLib) RemoveLocalTransaction(id int) {
|
||||
self.miner.RemoveLocalTx(id)
|
||||
}
|
||||
|
||||
func (self *UiLib) SetGasPrice(price string) {
|
||||
self.miner.MinAcceptedGasPrice = ethutil.Big(price)
|
||||
}
|
||||
|
||||
func (self *UiLib) ToggleMining() bool {
|
||||
if !self.miner.Mining() {
|
||||
self.miner.Start()
|
||||
|
||||
return true
|
||||
} else {
|
||||
self.miner.Stop()
|
||||
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
@ -266,7 +266,7 @@ func StartMining(ethereum *eth.Ethereum) bool {
|
||||
go func() {
|
||||
clilogger.Infoln("Start mining")
|
||||
if gminer == nil {
|
||||
gminer = miner.NewDefaultMiner(addr, ethereum)
|
||||
gminer = miner.New(addr, ethereum)
|
||||
}
|
||||
// Give it some time to connect with peers
|
||||
time.Sleep(3 * time.Second)
|
||||
|
Reference in New Issue
Block a user