mirror of https://github.com/ethereum/go-ethereum
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 miningpull/184/head
parent
48488017e4
commit
429dd2a100
After Width: | Height: | Size: 2.1 KiB |
@ -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 |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
@ -1,220 +1,230 @@ |
||||
package miner |
||||
|
||||
import ( |
||||
"bytes" |
||||
"math/big" |
||||
"sort" |
||||
|
||||
"github.com/ethereum/go-ethereum" |
||||
"github.com/ethereum/go-ethereum/ethutil" |
||||
|
||||
"github.com/ethereum/go-ethereum/chain" |
||||
"github.com/ethereum/go-ethereum/event" |
||||
"github.com/ethereum/go-ethereum/logger" |
||||
"github.com/ethereum/go-ethereum/wire" |
||||
) |
||||
|
||||
type LocalTx struct { |
||||
To []byte `json:"to"` |
||||
Data []byte `json:"data"` |
||||
Gas string `json:"gas"` |
||||
GasPrice string `json:"gasPrice"` |
||||
Value string `json:"value"` |
||||
} |
||||
|
||||
func (self *LocalTx) Sign(key []byte) *chain.Transaction { |
||||
return nil |
||||
} |
||||
|
||||
var minerlogger = logger.NewLogger("MINER") |
||||
|
||||
type Miner struct { |
||||
pow chain.PoW |
||||
ethereum chain.EthManager |
||||
coinbase []byte |
||||
txs chain.Transactions |
||||
uncles []*chain.Block |
||||
block *chain.Block |
||||
|
||||
events event.Subscription |
||||
powQuitChan chan struct{} |
||||
powDone chan struct{} |
||||
|
||||
turbo bool |
||||
} |
||||
eth *eth.Ethereum |
||||
events event.Subscription |
||||
|
||||
const ( |
||||
Started = iota |
||||
Stopped |
||||
) |
||||
uncles chain.Blocks |
||||
localTxs map[int]*LocalTx |
||||
localTxId int |
||||
|
||||
pow chain.PoW |
||||
quitCh chan struct{} |
||||
powQuitCh chan struct{} |
||||
|
||||
Coinbase []byte |
||||
|
||||
type Event struct { |
||||
Type int // Started || Stopped
|
||||
Miner *Miner |
||||
mining bool |
||||
|
||||
MinAcceptedGasPrice *big.Int |
||||
} |
||||
|
||||
func New(coinbase []byte, eth *eth.Ethereum) *Miner { |
||||
return &Miner{ |
||||
eth: eth, |
||||
powQuitCh: make(chan struct{}), |
||||
pow: &chain.EasyPow{}, |
||||
mining: false, |
||||
localTxs: make(map[int]*LocalTx), |
||||
MinAcceptedGasPrice: big.NewInt(10000000000000), |
||||
Coinbase: coinbase, |
||||
} |
||||
} |
||||
|
||||
func (self *Miner) GetPow() chain.PoW { |
||||
return self.pow |
||||
} |
||||
|
||||
func NewDefaultMiner(coinbase []byte, ethereum chain.EthManager) *Miner { |
||||
miner := Miner{ |
||||
pow: &chain.EasyPow{}, |
||||
ethereum: ethereum, |
||||
coinbase: coinbase, |
||||
func (self *Miner) AddLocalTx(tx *LocalTx) int { |
||||
minerlogger.Infof("Added local tx (%x %v / %v)\n", tx.To[0:4], tx.GasPrice, tx.Value) |
||||
|
||||
self.localTxId++ |
||||
self.localTxs[self.localTxId] = tx |
||||
self.eth.EventMux().Post(tx) |
||||
|
||||
return self.localTxId |
||||
} |
||||
|
||||
func (self *Miner) RemoveLocalTx(id int) { |
||||
if tx := self.localTxs[id]; tx != nil { |
||||
minerlogger.Infof("Removed local tx (%x %v / %v)\n", tx.To[0:4], tx.GasPrice, tx.Value) |
||||
} |
||||
self.eth.EventMux().Post(&LocalTx{}) |
||||
|
||||
return &miner |
||||
delete(self.localTxs, id) |
||||
} |
||||
|
||||
func (self *Miner) ToggleTurbo() { |
||||
self.turbo = !self.turbo |
||||
func (self *Miner) Start() { |
||||
if self.mining { |
||||
return |
||||
} |
||||
|
||||
self.pow.Turbo(self.turbo) |
||||
minerlogger.Infoln("Starting mining operations") |
||||
self.mining = true |
||||
self.quitCh = make(chan struct{}) |
||||
self.powQuitCh = make(chan struct{}) |
||||
|
||||
mux := self.eth.EventMux() |
||||
self.events = mux.Subscribe(chain.NewBlockEvent{}, chain.TxPreEvent{}, &LocalTx{}) |
||||
|
||||
go self.update() |
||||
go self.mine() |
||||
} |
||||
|
||||
func (miner *Miner) Start() { |
||||
func (self *Miner) Stop() { |
||||
if !self.mining { |
||||
return |
||||
} |
||||
|
||||
// Insert initial TXs in our little miner 'pool'
|
||||
miner.txs = miner.ethereum.TxPool().Flush() |
||||
miner.block = miner.ethereum.ChainManager().NewBlock(miner.coinbase) |
||||
self.mining = false |
||||
|
||||
mux := miner.ethereum.EventMux() |
||||
miner.events = mux.Subscribe(chain.NewBlockEvent{}, chain.TxPreEvent{}) |
||||
minerlogger.Infoln("Stopping mining operations") |
||||
|
||||
// Prepare inital block
|
||||
//miner.ethereum.BlockManager().Prepare(miner.block.State(), miner.block.State())
|
||||
go miner.listener() |
||||
self.events.Unsubscribe() |
||||
|
||||
minerlogger.Infoln("Started") |
||||
mux.Post(Event{Started, miner}) |
||||
close(self.quitCh) |
||||
close(self.powQuitCh) |
||||
} |
||||
|
||||
func (miner *Miner) Stop() { |
||||
minerlogger.Infoln("Stopping...") |
||||
miner.events.Unsubscribe() |
||||
miner.ethereum.EventMux().Post(Event{Stopped, miner}) |
||||
func (self *Miner) Mining() bool { |
||||
return self.mining |
||||
} |
||||
|
||||
func (miner *Miner) listener() { |
||||
miner.startMining() |
||||
|
||||
func (self *Miner) update() { |
||||
out: |
||||
for { |
||||
select { |
||||
case event := <-miner.events.Chan(): |
||||
case event := <-self.events.Chan(): |
||||
switch event := event.(type) { |
||||
case chain.NewBlockEvent: |
||||
miner.stopMining() |
||||
|
||||
block := event.Block |
||||
//minerlogger.Infoln("Got new block via Reactor")
|
||||
if bytes.Compare(miner.ethereum.ChainManager().CurrentBlock.Hash(), block.Hash()) == 0 { |
||||
// TODO: Perhaps continue mining to get some uncle rewards
|
||||
//minerlogger.Infoln("New top block found resetting state")
|
||||
|
||||
// Filter out which Transactions we have that were not in this block
|
||||
var newtxs []*chain.Transaction |
||||
for _, tx := range miner.txs { |
||||
found := false |
||||
for _, othertx := range block.Transactions() { |
||||
if bytes.Compare(tx.Hash(), othertx.Hash()) == 0 { |
||||
found = true |
||||
} |
||||
} |
||||
if found == false { |
||||
newtxs = append(newtxs, tx) |
||||
} |
||||
} |
||||
miner.txs = newtxs |
||||
} else { |
||||
if bytes.Compare(block.PrevHash, miner.ethereum.ChainManager().CurrentBlock.PrevHash) == 0 { |
||||
minerlogger.Infoln("Adding uncle block") |
||||
miner.uncles = append(miner.uncles, block) |
||||
} |
||||
} |
||||
miner.startMining() |
||||
|
||||
case chain.TxPreEvent: |
||||
miner.stopMining() |
||||
|
||||
found := false |
||||
for _, ctx := range miner.txs { |
||||
if found = bytes.Compare(ctx.Hash(), event.Tx.Hash()) == 0; found { |
||||
break |
||||
} |
||||
|
||||
miner.startMining() |
||||
} |
||||
if found == false { |
||||
// Undo all previous commits
|
||||
miner.block.Undo() |
||||
// Apply new transactions
|
||||
miner.txs = append(miner.txs, event.Tx) |
||||
if self.eth.ChainManager().HasBlock(block.Hash()) { |
||||
self.reset() |
||||
self.eth.TxPool().RemoveSet(block.Transactions()) |
||||
go self.mine() |
||||
} else if true { |
||||
// do uncle stuff
|
||||
} |
||||
case chain.TxPreEvent, *LocalTx: |
||||
self.reset() |
||||
go self.mine() |
||||
} |
||||
|
||||
case <-miner.powDone: |
||||
miner.startMining() |
||||
case <-self.quitCh: |
||||
break out |
||||
} |
||||
} |
||||
} |
||||
|
||||
func (miner *Miner) startMining() { |
||||
if miner.powDone == nil { |
||||
miner.powDone = make(chan struct{}) |
||||
} |
||||
miner.powQuitChan = make(chan struct{}) |
||||
go miner.mineNewBlock() |
||||
} |
||||
|
||||
func (miner *Miner) stopMining() { |
||||
println("stop mining") |
||||
_, isopen := <-miner.powQuitChan |
||||
if isopen { |
||||
close(miner.powQuitChan) |
||||
} |
||||
//<-miner.powDone
|
||||
func (self *Miner) reset() { |
||||
println("reset") |
||||
close(self.powQuitCh) |
||||
self.powQuitCh = make(chan struct{}) |
||||
} |
||||
|
||||
func (self *Miner) mineNewBlock() { |
||||
blockManager := self.ethereum.BlockManager() |
||||
chainMan := self.ethereum.ChainManager() |
||||
|
||||
self.block = chainMan.NewBlock(self.coinbase) |
||||
func (self *Miner) mine() { |
||||
var ( |
||||
blockManager = self.eth.BlockManager() |
||||
chainMan = self.eth.ChainManager() |
||||
block = chainMan.NewBlock(self.Coinbase) |
||||
) |
||||
block.MinGasPrice = self.MinAcceptedGasPrice |
||||
|
||||
// Apply uncles
|
||||
if len(self.uncles) > 0 { |
||||
self.block.SetUncles(self.uncles) |
||||
block.SetUncles(self.uncles) |
||||
} |
||||
|
||||
// Sort the transactions by nonce in case of odd network propagation
|
||||
sort.Sort(chain.TxByNonce{self.txs}) |
||||
parent := chainMan.GetBlock(block.PrevHash) |
||||
coinbase := block.State().GetOrNewStateObject(block.Coinbase) |
||||
coinbase.SetGasPool(block.CalcGasLimit(parent)) |
||||
|
||||
transactions := self.finiliseTxs() |
||||
|
||||
// Accumulate all valid transactions and apply them to the new state
|
||||
// Error may be ignored. It's not important during mining
|
||||
parent := self.ethereum.ChainManager().GetBlock(self.block.PrevHash) |
||||
coinbase := self.block.State().GetOrNewStateObject(self.block.Coinbase) |
||||
coinbase.SetGasPool(self.block.CalcGasLimit(parent)) |
||||
receipts, txs, unhandledTxs, erroneous, err := blockManager.ProcessTransactions(coinbase, self.block.State(), self.block, self.block, self.txs) |
||||
receipts, txs, _, erroneous, err := blockManager.ProcessTransactions(coinbase, block.State(), block, block, transactions) |
||||
if err != nil { |
||||
minerlogger.Debugln(err) |
||||
} |
||||
self.ethereum.TxPool().RemoveSet(erroneous) |
||||
self.txs = append(txs, unhandledTxs...) |
||||
self.eth.TxPool().RemoveSet(erroneous) |
||||
|
||||
self.block.SetTransactions(txs) |
||||
self.block.SetReceipts(receipts) |
||||
block.SetTransactions(txs) |
||||
block.SetReceipts(receipts) |
||||
|
||||
// Accumulate the rewards included for this block
|
||||
blockManager.AccumelateRewards(self.block.State(), self.block, parent) |
||||
blockManager.AccumelateRewards(block.State(), block, parent) |
||||
|
||||
self.block.State().Update() |
||||
block.State().Update() |
||||
|
||||
minerlogger.Infof("Mining on block. Includes %v transactions", len(self.txs)) |
||||
minerlogger.Infof("Mining on block. Includes %v transactions", len(transactions)) |
||||
|
||||
// Find a valid nonce
|
||||
nonce := self.pow.Search(self.block, self.powQuitChan) |
||||
nonce := self.pow.Search(block, self.powQuitCh) |
||||
if nonce != nil { |
||||
self.block.Nonce = nonce |
||||
lchain := chain.NewChain(chain.Blocks{self.block}) |
||||
_, err := chainMan.TestChain(lchain) |
||||
block.Nonce = nonce |
||||
lchain := chain.NewChain(chain.Blocks{block}) |
||||
_, err := chainMan.TestChain(lchain, true) |
||||
if err != nil { |
||||
minerlogger.Infoln(err) |
||||
} else { |
||||
self.ethereum.ChainManager().InsertChain(lchain) |
||||
self.ethereum.Broadcast(wire.MsgBlockTy, []interface{}{self.block.Value().Val}) |
||||
minerlogger.Infof("🔨 Mined block %x\n", self.block.Hash()) |
||||
minerlogger.Infoln(self.block) |
||||
// Gather the new batch of transactions currently in the tx pool
|
||||
self.txs = self.ethereum.TxPool().CurrentTransactions() |
||||
self.ethereum.EventMux().Post(chain.NewBlockEvent{self.block}) |
||||
//chainMan.InsertChain(lchain)
|
||||
self.eth.EventMux().Post(chain.NewBlockEvent{block}) |
||||
self.eth.Broadcast(wire.MsgBlockTy, []interface{}{block.Value().Val}) |
||||
|
||||
minerlogger.Infof("🔨 Mined block %x\n", block.Hash()) |
||||
minerlogger.Infoln(block) |
||||
} |
||||
|
||||
// Continue mining on the next block
|
||||
self.startMining() |
||||
go self.mine() |
||||
} |
||||
} |
||||
|
||||
func (self *Miner) finiliseTxs() chain.Transactions { |
||||
// Sort the transactions by nonce in case of odd network propagation
|
||||
var txs chain.Transactions |
||||
|
||||
state := self.eth.BlockManager().TransState() |
||||
// XXX This has to change. Coinbase is, for new, same as key.
|
||||
key := self.eth.KeyManager() |
||||
for _, ltx := range self.localTxs { |
||||
tx := chain.NewTransactionMessage(ltx.To, ethutil.Big(ltx.Value), ethutil.Big(ltx.Gas), ethutil.Big(ltx.GasPrice), ltx.Data) |
||||
tx.Nonce = state.GetNonce(self.Coinbase) |
||||
state.SetNonce(self.Coinbase, tx.Nonce+1) |
||||
|
||||
tx.Sign(key.PrivateKey()) |
||||
|
||||
txs = append(txs, tx) |
||||
} |
||||
|
||||
txs = append(txs, self.eth.TxPool().CurrentTransactions()...) |
||||
sort.Sort(chain.TxByNonce{txs}) |
||||
|
||||
return txs |
||||
} |
||||
|
Loading…
Reference in new issue