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:
obscuren 2014-11-07 12:18:48 +01:00
parent 48488017e4
commit 429dd2a100
18 changed files with 558 additions and 233 deletions

View File

@ -315,9 +315,12 @@ out:
// otherwise process and don't emit anything // otherwise process and don't emit anything
if len(blocks) > 0 { if len(blocks) > 0 {
chainManager := self.eth.ChainManager() chainManager := self.eth.ChainManager()
// Test and import
chain := chain.NewChain(blocks) chain := chain.NewChain(blocks)
_, err := chainManager.TestChain(chain) _, err := chainManager.TestChain(chain, true)
if err != nil { if err != nil {
poollogger.Debugln(err)
self.Reset() self.Reset()
poollogger.Debugf("Punishing peer for supplying bad chain (%v)\n", self.peer.conn.RemoteAddr()) poollogger.Debugf("Punishing peer for supplying bad chain (%v)\n", self.peer.conn.RemoteAddr())
@ -327,7 +330,7 @@ out:
self.td = ethutil.Big0 self.td = ethutil.Big0
self.peer = nil self.peer = nil
} else { } else {
chainManager.InsertChain(chain) //chainManager.InsertChain(chain)
for _, block := range blocks { for _, block := range blocks {
self.Remove(block.Hash()) self.Remove(block.Hash())
} }

View File

@ -228,7 +228,7 @@ func (sm *BlockManager) Process(block *Block) (td *big.Int, err error) {
func (sm *BlockManager) ProcessWithParent(block, parent *Block) (td *big.Int, err error) { func (sm *BlockManager) ProcessWithParent(block, parent *Block) (td *big.Int, err error) {
sm.lastAttemptedBlock = block sm.lastAttemptedBlock = block
state := parent.State() state := parent.State().Copy()
// Defer the Undo on the Trie. If the block processing happened // Defer the Undo on the Trie. If the block processing happened
// we don't want to undo but since undo only happens on dirty // we don't want to undo but since undo only happens on dirty
@ -240,20 +240,22 @@ func (sm *BlockManager) ProcessWithParent(block, parent *Block) (td *big.Int, er
fmt.Printf("## %x %x ##\n", block.Hash(), block.Number) fmt.Printf("## %x %x ##\n", block.Hash(), block.Number)
} }
_, err = sm.ApplyDiff(state, parent, block)
if err != nil {
return nil, err
}
/* Go and C++ don't have consensus here. FIXME
txSha := DeriveSha(block.transactions) txSha := DeriveSha(block.transactions)
if bytes.Compare(txSha, block.TxSha) != 0 { if bytes.Compare(txSha, block.TxSha) != 0 {
return nil, fmt.Errorf("Error validating transaction sha. Received %x, got %x", block.TxSha, txSha) return nil, fmt.Errorf("Error validating transaction sha. Received %x, got %x", block.TxSha, txSha)
} }
receipts, err := sm.ApplyDiff(state, parent, block)
if err != nil {
return nil, err
}
receiptSha := DeriveSha(receipts) receiptSha := DeriveSha(receipts)
if bytes.Compare(receiptSha, block.ReceiptSha) != 0 { if bytes.Compare(receiptSha, block.ReceiptSha) != 0 {
return nil, fmt.Errorf("Error validating receipt sha. Received %x, got %x", block.ReceiptSha, receiptSha) return nil, fmt.Errorf("Error validating receipt sha. Received %x, got %x", block.ReceiptSha, receiptSha)
} }
*/
// Block validation // Block validation
if err = sm.ValidateBlock(block, parent); err != nil { if err = sm.ValidateBlock(block, parent); err != nil {
@ -374,7 +376,7 @@ func (sm *BlockManager) AccumelateRewards(state *state.State, block, parent *Blo
uncleParent := sm.bc.GetBlock(uncle.PrevHash) uncleParent := sm.bc.GetBlock(uncle.PrevHash)
if uncleParent == nil { if uncleParent == nil {
return UncleError("Uncle's parent unknown") return UncleError(fmt.Sprintf("Uncle's parent unknown (%x)", uncle.PrevHash[0:4]))
} }
if uncleParent.Number.Cmp(new(big.Int).Sub(parent.Number, big.NewInt(6))) < 0 { if uncleParent.Number.Cmp(new(big.Int).Sub(parent.Number, big.NewInt(6))) < 0 {

View File

@ -23,6 +23,8 @@ type ChainManager struct {
CurrentBlock *Block CurrentBlock *Block
LastBlockHash []byte LastBlockHash []byte
workingChain *BlockChain
} }
func NewChainManager(ethereum EthManager) *ChainManager { func NewChainManager(ethereum EthManager) *ChainManager {
@ -225,9 +227,18 @@ func (self *ChainManager) CalcTotalDiff(block *Block) (*big.Int, error) {
return td, nil return td, nil
} }
func (bc *ChainManager) GetBlock(hash []byte) *Block { func (self *ChainManager) GetBlock(hash []byte) *Block {
data, _ := ethutil.Config.Db.Get(hash) data, _ := ethutil.Config.Db.Get(hash)
if len(data) == 0 { if len(data) == 0 {
if self.workingChain != nil {
// Check the temp chain
for e := self.workingChain.Front(); e != nil; e = e.Next() {
if bytes.Compare(e.Value.(*link).block.Hash(), hash) == 0 {
return e.Value.(*link).block
}
}
}
return nil return nil
} }
@ -310,6 +321,7 @@ func NewChain(blocks Blocks) *BlockChain {
} }
// This function assumes you've done your checking. No checking is done at this stage anymore // This function assumes you've done your checking. No checking is done at this stage anymore
/*
func (self *ChainManager) InsertChain(chain *BlockChain) { func (self *ChainManager) InsertChain(chain *BlockChain) {
for e := chain.Front(); e != nil; e = e.Next() { for e := chain.Front(); e != nil; e = e.Next() {
link := e.Value.(*link) link := e.Value.(*link)
@ -318,8 +330,11 @@ func (self *ChainManager) InsertChain(chain *BlockChain) {
self.add(link.block) self.add(link.block)
} }
} }
*/
func (self *ChainManager) TestChain(chain *BlockChain, imp bool) (td *big.Int, err error) {
self.workingChain = chain
func (self *ChainManager) TestChain(chain *BlockChain) (td *big.Int, err error) {
for e := chain.Front(); e != nil; e = e.Next() { for e := chain.Front(); e != nil; e = e.Next() {
var ( var (
l = e.Value.(*link) l = e.Value.(*link)
@ -348,12 +363,21 @@ func (self *ChainManager) TestChain(chain *BlockChain) (td *big.Int, err error)
return return
} }
l.td = td l.td = td
if imp {
self.SetTotalDifficulty(td)
self.add(block)
}
} }
if !imp {
if td.Cmp(self.TD) <= 0 { if td.Cmp(self.TD) <= 0 {
err = &TDError{td, self.TD} err = &TDError{td, self.TD}
return return
} }
}
self.workingChain = nil
return return
} }

View File

@ -47,6 +47,7 @@ func (pow *EasyPow) Search(block *Block, stop <-chan struct{}) []byte {
select { select {
case <-stop: case <-stop:
powlogger.Infoln("Breaking from mining") powlogger.Infoln("Breaking from mining")
pow.HashRate = 0
return nil return nil
default: default:
i++ i++
@ -55,7 +56,7 @@ func (pow *EasyPow) Search(block *Block, stop <-chan struct{}) []byte {
elapsed := time.Now().UnixNano() - start elapsed := time.Now().UnixNano() - start
hashes := ((float64(1e9) / float64(elapsed)) * float64(i)) / 1000 hashes := ((float64(1e9) / float64(elapsed)) * float64(i)) / 1000
pow.HashRate = int64(hashes) pow.HashRate = int64(hashes)
powlogger.Infoln("Hashing @", int64(pow.HashRate), "khash") powlogger.Infoln("Hashing @", pow.HashRate, "khash")
t = time.Now() t = time.Now()
} }

BIN
cmd/mist/assets/miner.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@ -12,7 +12,6 @@ import "../ext/http.js" as Http
ApplicationWindow { ApplicationWindow {
id: root id: root
property alias miningButtonText: miningButton.text
property var ethx : Eth.ethx property var ethx : Eth.ethx
property var browser property var browser
@ -47,6 +46,7 @@ ApplicationWindow {
Component.onCompleted: { Component.onCompleted: {
var wallet = addPlugin("./views/wallet.qml", {noAdd: true, close: false, section: "ethereum", active: true}); 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("./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; root.browser = browser;
addPlugin("./views/transaction.qml", {noAdd: true, close: false, section: "legacy"}); addPlugin("./views/transaction.qml", {noAdd: true, close: false, section: "legacy"});
@ -252,29 +252,18 @@ ApplicationWindow {
} }
statusBar: StatusBar { statusBar: StatusBar {
height: 32 //height: 32
id: statusBar id: statusBar
RowLayout {
Button {
id: miningButton
text: "Start Mining"
onClicked: {
gui.toggleMining()
}
}
RowLayout {
Label { Label {
//y: 6
id: walletValueLabel id: walletValueLabel
font.pixelSize: 10 font.pixelSize: 10
styleColor: "#797979" styleColor: "#797979"
} }
}
}
Label { Label {
y: 6 //y: 6
objectName: "miningLabel" objectName: "miningLabel"
visible: true visible: true
font.pixelSize: 10 font.pixelSize: 10
@ -283,7 +272,7 @@ ApplicationWindow {
} }
Label { Label {
y: 6 //y: 6
id: lastBlockLabel id: lastBlockLabel
objectName: "lastBlockLabel" objectName: "lastBlockLabel"
visible: true visible: true
@ -297,14 +286,14 @@ ApplicationWindow {
id: downloadIndicator id: downloadIndicator
value: 0 value: 0
objectName: "downloadIndicator" objectName: "downloadIndicator"
y: 3 y: -4
x: statusBar.width / 2 - this.width / 2 x: statusBar.width / 2 - this.width / 2
width: 160 width: 160
} }
Label { Label {
objectName: "downloadLabel" objectName: "downloadLabel"
y: 7 //y: 7
anchors.left: downloadIndicator.right anchors.left: downloadIndicator.right
anchors.leftMargin: 5 anchors.leftMargin: 5
font.pixelSize: 10 font.pixelSize: 10
@ -314,7 +303,7 @@ ApplicationWindow {
RowLayout { RowLayout {
id: peerGroup id: peerGroup
y: 7 //y: 7
anchors.right: parent.right anchors.right: parent.right
MouseArea { MouseArea {
onDoubleClicked: peerWindow.visible = true onDoubleClicked: peerWindow.visible = true
@ -323,14 +312,9 @@ ApplicationWindow {
Label { Label {
id: peerLabel id: peerLabel
font.pixelSize: 8 font.pixelSize: 10
text: "0 / 0" text: "0 / 0"
} }
Image {
id: peerImage
width: 10; height: 10
source: "../network.png"
}
} }
} }

View 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
}
}
}
}
}
}
}

View File

@ -70,10 +70,6 @@ func (gui *Gui) GetCustomIdentifier() string {
return gui.clientIdentity.GetCustomIdentifier() return gui.clientIdentity.GetCustomIdentifier()
} }
func (gui *Gui) ToggleTurboMining() {
gui.miner.ToggleTurbo()
}
// functions that allow Gui to implement interface guilogger.LogSystem // functions that allow Gui to implement interface guilogger.LogSystem
func (gui *Gui) SetLogLevel(level logger.LogLevel) { func (gui *Gui) SetLogLevel(level logger.LogLevel) {
gui.logLevel = level gui.logLevel = level
@ -137,20 +133,3 @@ func (self *Gui) DumpState(hash, path string) {
file.Write(stateDump) 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)
}

View File

@ -272,8 +272,6 @@ type address struct {
func (gui *Gui) loadAddressBook() { func (gui *Gui) loadAddressBook() {
view := gui.getObjectByName("infoView") view := gui.getObjectByName("infoView")
view.Call("clearAddress")
nameReg := gui.pipe.World().Config().Get("NameReg") nameReg := gui.pipe.World().Config().Get("NameReg")
if nameReg != nil { if nameReg != nil {
nameReg.EachStorage(func(name string, value *ethutil.Value) { 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) { func (gui *Gui) insertTransaction(window string, tx *chain.Transaction) {
pipe := xeth.New(gui.eth) pipe := xeth.New(gui.eth)
nameReg := pipe.World().Config().Get("NameReg") nameReg := pipe.World().Config().Get("NameReg")
@ -382,6 +402,7 @@ func (gui *Gui) update() {
go func() { go func() {
go gui.setInitialChainManager() go gui.setInitialChainManager()
gui.loadAddressBook() gui.loadAddressBook()
gui.loadMergedMiningOptions()
gui.setPeerInfo() gui.setPeerInfo()
gui.readPreviousTransactions() gui.readPreviousTransactions()
}() }()
@ -410,7 +431,6 @@ func (gui *Gui) update() {
chain.NewBlockEvent{}, chain.NewBlockEvent{},
chain.TxPreEvent{}, chain.TxPreEvent{},
chain.TxPostEvent{}, chain.TxPostEvent{},
miner.Event{},
) )
// nameReg := gui.pipe.World().Config().Get("NameReg") // nameReg := gui.pipe.World().Config().Get("NameReg")
@ -469,12 +489,14 @@ func (gui *Gui) update() {
case eth.PeerListEvent: case eth.PeerListEvent:
gui.setPeerInfo() gui.setPeerInfo()
/*
case miner.Event: case miner.Event:
if ev.Type == miner.Started { if ev.Type == miner.Started {
gui.miner = ev.Miner gui.miner = ev.Miner
} else { } else {
gui.miner = nil gui.miner = nil
} }
*/
} }
case <-peerUpdateTicker.C: case <-peerUpdateTicker.C:
@ -483,10 +505,13 @@ func (gui *Gui) update() {
statusText := "#" + gui.eth.ChainManager().CurrentBlock.Number.String() statusText := "#" + gui.eth.ChainManager().CurrentBlock.Number.String()
lastBlockLabel.Set("text", statusText) lastBlockLabel.Set("text", statusText)
miningLabel.Set("text", "Mining @ "+strconv.FormatInt(gui.uiLib.miner.GetPow().GetHashrate(), 10)+"Khash")
/*
if gui.miner != nil { if gui.miner != nil {
pow := gui.miner.GetPow() pow := gui.miner.GetPow()
miningLabel.Set("text", "Mining @ "+strconv.FormatInt(pow.GetHashrate(), 10)+"Khash") miningLabel.Set("text", "Mining @ "+strconv.FormatInt(pow.GetHashrate(), 10)+"Khash")
} }
*/
blockLength := gui.eth.BlockPool().BlocksProcessed blockLength := gui.eth.BlockPool().BlocksProcessed
chainLength := gui.eth.BlockPool().ChainLength chainLength := gui.eth.BlockPool().ChainLength

View File

@ -29,6 +29,7 @@ import (
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethutil" "github.com/ethereum/go-ethereum/ethutil"
"github.com/ethereum/go-ethereum/javascript" "github.com/ethereum/go-ethereum/javascript"
"github.com/ethereum/go-ethereum/miner"
"github.com/ethereum/go-ethereum/state" "github.com/ethereum/go-ethereum/state"
"github.com/ethereum/go-ethereum/ui/qt" "github.com/ethereum/go-ethereum/ui/qt"
"github.com/ethereum/go-ethereum/xeth" "github.com/ethereum/go-ethereum/xeth"
@ -55,10 +56,15 @@ type UiLib struct {
jsEngine *javascript.JSRE jsEngine *javascript.JSRE
filterCallbacks map[int][]int filterCallbacks map[int][]int
miner *miner.Miner
} }
func NewUiLib(engine *qml.Engine, eth *eth.Ethereum, assetPath string) *UiLib { 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{}) { func (self *UiLib) Notef(args []interface{}) {
@ -328,3 +334,33 @@ func (self *UiLib) Call(params map[string]interface{}) (string, error) {
object["data"], 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
}
}

View File

@ -266,7 +266,7 @@ func StartMining(ethereum *eth.Ethereum) bool {
go func() { go func() {
clilogger.Infoln("Start mining") clilogger.Infoln("Start mining")
if gminer == nil { if gminer == nil {
gminer = miner.NewDefaultMiner(addr, ethereum) gminer = miner.New(addr, ethereum)
} }
// Give it some time to connect with peers // Give it some time to connect with peers
time.Sleep(3 * time.Second) time.Sleep(3 * time.Second)

View File

@ -1,220 +1,230 @@
package miner package miner
import ( import (
"bytes" "math/big"
"sort" "sort"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/ethutil"
"github.com/ethereum/go-ethereum/chain" "github.com/ethereum/go-ethereum/chain"
"github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/wire" "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") var minerlogger = logger.NewLogger("MINER")
type Miner struct { type Miner struct {
pow chain.PoW eth *eth.Ethereum
ethereum chain.EthManager
coinbase []byte
txs chain.Transactions
uncles []*chain.Block
block *chain.Block
events event.Subscription events event.Subscription
powQuitChan chan struct{}
powDone chan struct{}
turbo bool uncles chain.Blocks
localTxs map[int]*LocalTx
localTxId int
pow chain.PoW
quitCh chan struct{}
powQuitCh chan struct{}
Coinbase []byte
mining bool
MinAcceptedGasPrice *big.Int
} }
const ( func New(coinbase []byte, eth *eth.Ethereum) *Miner {
Started = iota return &Miner{
Stopped eth: eth,
) powQuitCh: make(chan struct{}),
pow: &chain.EasyPow{},
type Event struct { mining: false,
Type int // Started || Stopped localTxs: make(map[int]*LocalTx),
Miner *Miner MinAcceptedGasPrice: big.NewInt(10000000000000),
Coinbase: coinbase,
}
} }
func (self *Miner) GetPow() chain.PoW { func (self *Miner) GetPow() chain.PoW {
return self.pow return self.pow
} }
func NewDefaultMiner(coinbase []byte, ethereum chain.EthManager) *Miner { func (self *Miner) AddLocalTx(tx *LocalTx) int {
miner := Miner{ minerlogger.Infof("Added local tx (%x %v / %v)\n", tx.To[0:4], tx.GasPrice, tx.Value)
pow: &chain.EasyPow{},
ethereum: ethereum, self.localTxId++
coinbase: coinbase, 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{})
delete(self.localTxs, id)
}
func (self *Miner) Start() {
if self.mining {
return
} }
return &miner 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 (self *Miner) ToggleTurbo() { func (self *Miner) Stop() {
self.turbo = !self.turbo if !self.mining {
return
}
self.pow.Turbo(self.turbo) self.mining = false
minerlogger.Infoln("Stopping mining operations")
self.events.Unsubscribe()
close(self.quitCh)
close(self.powQuitCh)
} }
func (miner *Miner) Start() { func (self *Miner) Mining() bool {
return self.mining
// Insert initial TXs in our little miner 'pool'
miner.txs = miner.ethereum.TxPool().Flush()
miner.block = miner.ethereum.ChainManager().NewBlock(miner.coinbase)
mux := miner.ethereum.EventMux()
miner.events = mux.Subscribe(chain.NewBlockEvent{}, chain.TxPreEvent{})
// Prepare inital block
//miner.ethereum.BlockManager().Prepare(miner.block.State(), miner.block.State())
go miner.listener()
minerlogger.Infoln("Started")
mux.Post(Event{Started, miner})
} }
func (miner *Miner) Stop() { func (self *Miner) update() {
minerlogger.Infoln("Stopping...") out:
miner.events.Unsubscribe()
miner.ethereum.EventMux().Post(Event{Stopped, miner})
}
func (miner *Miner) listener() {
miner.startMining()
for { for {
select { select {
case event := <-miner.events.Chan(): case event := <-self.events.Chan():
switch event := event.(type) { switch event := event.(type) {
case chain.NewBlockEvent: case chain.NewBlockEvent:
miner.stopMining()
block := event.Block block := event.Block
//minerlogger.Infoln("Got new block via Reactor") if self.eth.ChainManager().HasBlock(block.Hash()) {
if bytes.Compare(miner.ethereum.ChainManager().CurrentBlock.Hash(), block.Hash()) == 0 { self.reset()
// TODO: Perhaps continue mining to get some uncle rewards self.eth.TxPool().RemoveSet(block.Transactions())
//minerlogger.Infoln("New top block found resetting state") go self.mine()
} else if true {
// Filter out which Transactions we have that were not in this block // do uncle stuff
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
} }
case chain.TxPreEvent, *LocalTx:
self.reset()
go self.mine()
} }
if found == false { case <-self.quitCh:
newtxs = append(newtxs, tx) break out
}
}
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)
}
}
case <-miner.powDone:
miner.startMining()
} }
} }
} }
func (miner *Miner) startMining() { func (self *Miner) reset() {
if miner.powDone == nil { println("reset")
miner.powDone = make(chan struct{}) close(self.powQuitCh)
} self.powQuitCh = make(chan struct{})
miner.powQuitChan = make(chan struct{})
go miner.mineNewBlock()
} }
func (miner *Miner) stopMining() { func (self *Miner) mine() {
println("stop mining") var (
_, isopen := <-miner.powQuitChan blockManager = self.eth.BlockManager()
if isopen { chainMan = self.eth.ChainManager()
close(miner.powQuitChan) block = chainMan.NewBlock(self.Coinbase)
} )
//<-miner.powDone block.MinGasPrice = self.MinAcceptedGasPrice
}
func (self *Miner) mineNewBlock() {
blockManager := self.ethereum.BlockManager()
chainMan := self.ethereum.ChainManager()
self.block = chainMan.NewBlock(self.coinbase)
// Apply uncles // Apply uncles
if len(self.uncles) > 0 { 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 parent := chainMan.GetBlock(block.PrevHash)
sort.Sort(chain.TxByNonce{self.txs}) 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 // Accumulate all valid transactions and apply them to the new state
// Error may be ignored. It's not important during mining // Error may be ignored. It's not important during mining
parent := self.ethereum.ChainManager().GetBlock(self.block.PrevHash) receipts, txs, _, erroneous, err := blockManager.ProcessTransactions(coinbase, block.State(), block, block, transactions)
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)
if err != nil { if err != nil {
minerlogger.Debugln(err) minerlogger.Debugln(err)
} }
self.ethereum.TxPool().RemoveSet(erroneous) self.eth.TxPool().RemoveSet(erroneous)
self.txs = append(txs, unhandledTxs...)
self.block.SetTransactions(txs) block.SetTransactions(txs)
self.block.SetReceipts(receipts) block.SetReceipts(receipts)
// Accumulate the rewards included for this block // 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 // Find a valid nonce
nonce := self.pow.Search(self.block, self.powQuitChan) nonce := self.pow.Search(block, self.powQuitCh)
if nonce != nil { if nonce != nil {
self.block.Nonce = nonce block.Nonce = nonce
lchain := chain.NewChain(chain.Blocks{self.block}) lchain := chain.NewChain(chain.Blocks{block})
_, err := chainMan.TestChain(lchain) _, err := chainMan.TestChain(lchain, true)
if err != nil { if err != nil {
minerlogger.Infoln(err) minerlogger.Infoln(err)
} else { } else {
self.ethereum.ChainManager().InsertChain(lchain) //chainMan.InsertChain(lchain)
self.ethereum.Broadcast(wire.MsgBlockTy, []interface{}{self.block.Value().Val}) self.eth.EventMux().Post(chain.NewBlockEvent{block})
minerlogger.Infof("🔨 Mined block %x\n", self.block.Hash()) self.eth.Broadcast(wire.MsgBlockTy, []interface{}{block.Value().Val})
minerlogger.Infoln(self.block)
// Gather the new batch of transactions currently in the tx pool minerlogger.Infof("🔨 Mined block %x\n", block.Hash())
self.txs = self.ethereum.TxPool().CurrentTransactions() minerlogger.Infoln(block)
self.ethereum.EventMux().Post(chain.NewBlockEvent{self.block})
} }
// Continue mining on the next block go self.mine()
self.startMining()
} }
} }
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
}

View File

@ -6,7 +6,7 @@ import (
"net" "net"
"time" "time"
"github.com/ethereum/eth-go/ethutil" "github.com/ethereum/go-ethereum/ethutil"
) )
type Connection struct { type Connection struct {

View File

@ -2,7 +2,7 @@ package p2p
import ( import (
// "fmt" // "fmt"
"github.com/ethereum/eth-go/ethutil" "github.com/ethereum/go-ethereum/ethutil"
) )
type MsgCode uint8 type MsgCode uint8

View File

@ -3,9 +3,10 @@ package p2p
import ( import (
// "fmt" // "fmt"
"bytes" "bytes"
"github.com/ethereum/eth-go/ethutil"
"testing" "testing"
"time" "time"
"github.com/ethereum/go-ethereum/ethutil"
) )
func setupMessenger(handlers Handlers) (*TestNetworkConnection, chan *PeerError, *Messenger) { func setupMessenger(handlers Handlers) (*TestNetworkConnection, chan *PeerError, *Messenger) {

View File

@ -24,7 +24,7 @@ const (
// The size of the output buffer for writing messages // The size of the output buffer for writing messages
outputBufferSize = 50 outputBufferSize = 50
// Current protocol version // Current protocol version
ProtocolVersion = 37 ProtocolVersion = 39
// Current P2P version // Current P2P version
P2PVersion = 2 P2PVersion = 2
// Ethereum network version // Ethereum network version
@ -863,7 +863,7 @@ func (p *Peer) String() string {
strConnectType = "disconnected" strConnectType = "disconnected"
} }
return fmt.Sprintf("[%s] (%s) %v %s [%s]", strConnectType, strBoundType, p.conn.RemoteAddr(), p.version, p.caps) return fmt.Sprintf("[%s] (%s) %v %s", strConnectType, strBoundType, p.conn.RemoteAddr(), p.version)
} }

View File

@ -19,6 +19,8 @@ func (self *Config) Get(name string) *Object {
objectAddr := configCtrl.GetStorage(ethutil.BigD([]byte{0})) objectAddr := configCtrl.GetStorage(ethutil.BigD([]byte{0}))
domainAddr := (&Object{self.pipe.World().safeGet(objectAddr.Bytes())}).StorageString("DnsReg").Bytes() domainAddr := (&Object{self.pipe.World().safeGet(objectAddr.Bytes())}).StorageString("DnsReg").Bytes()
return &Object{self.pipe.World().safeGet(domainAddr)} return &Object{self.pipe.World().safeGet(domainAddr)}
case "MergeMining":
addr = []byte{4}
default: default:
addr = ethutil.RightPadBytes([]byte(name), 32) addr = ethutil.RightPadBytes([]byte(name), 32)
} }

View File

@ -254,6 +254,10 @@ func (self *JSXEth) CompileMutan(code string) string {
return ethutil.Bytes2Hex(data) return ethutil.Bytes2Hex(data)
} }
func (self *JSXEth) FindInConfig(str string) string {
return ethutil.Bytes2Hex(self.World().Config().Get(str).Address())
}
func ToJSMessages(messages state.Messages) *ethutil.List { func ToJSMessages(messages state.Messages) *ethutil.List {
var msgs []JSMessage var msgs []JSMessage
for _, m := range messages { for _, m := range messages {