forked from mirror/go-ethereum
parent
4914a78c8c
commit
5af4ff985d
@ -0,0 +1,217 @@ |
|||||||
|
package miner |
||||||
|
|
||||||
|
import ( |
||||||
|
"bytes" |
||||||
|
"sort" |
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/chain" |
||||||
|
"github.com/ethereum/go-ethereum/event" |
||||||
|
"github.com/ethereum/go-ethereum/logger" |
||||||
|
"github.com/ethereum/go-ethereum/wire" |
||||||
|
) |
||||||
|
|
||||||
|
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 |
||||||
|
} |
||||||
|
|
||||||
|
const ( |
||||||
|
Started = iota |
||||||
|
Stopped |
||||||
|
) |
||||||
|
|
||||||
|
type Event struct { |
||||||
|
Type int // Started || Stopped
|
||||||
|
Miner *Miner |
||||||
|
} |
||||||
|
|
||||||
|
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, |
||||||
|
} |
||||||
|
|
||||||
|
return &miner |
||||||
|
} |
||||||
|
|
||||||
|
func (self *Miner) ToggleTurbo() { |
||||||
|
self.turbo = !self.turbo |
||||||
|
|
||||||
|
self.pow.Turbo(self.turbo) |
||||||
|
} |
||||||
|
|
||||||
|
func (miner *Miner) Start() { |
||||||
|
|
||||||
|
// 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.StateManager().Prepare(miner.block.State(), miner.block.State())
|
||||||
|
go miner.listener() |
||||||
|
|
||||||
|
minerlogger.Infoln("Started") |
||||||
|
mux.Post(Event{Started, miner}) |
||||||
|
} |
||||||
|
|
||||||
|
func (miner *Miner) Stop() { |
||||||
|
minerlogger.Infoln("Stopping...") |
||||||
|
miner.events.Unsubscribe() |
||||||
|
miner.ethereum.EventMux().Post(Event{Stopped, miner}) |
||||||
|
} |
||||||
|
|
||||||
|
func (miner *Miner) listener() { |
||||||
|
miner.startMining() |
||||||
|
|
||||||
|
for { |
||||||
|
select { |
||||||
|
case event := <-miner.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) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
case <-miner.powDone: |
||||||
|
miner.startMining() |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
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) mineNewBlock() { |
||||||
|
stateManager := self.ethereum.StateManager() |
||||||
|
|
||||||
|
self.block = self.ethereum.ChainManager().NewBlock(self.coinbase) |
||||||
|
|
||||||
|
// Apply uncles
|
||||||
|
if len(self.uncles) > 0 { |
||||||
|
self.block.SetUncles(self.uncles) |
||||||
|
} |
||||||
|
|
||||||
|
// Sort the transactions by nonce in case of odd network propagation
|
||||||
|
sort.Sort(chain.TxByNonce{self.txs}) |
||||||
|
|
||||||
|
// 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 := stateManager.ProcessTransactions(coinbase, self.block.State(), self.block, self.block, self.txs) |
||||||
|
if err != nil { |
||||||
|
minerlogger.Debugln(err) |
||||||
|
} |
||||||
|
self.ethereum.TxPool().RemoveSet(erroneous) |
||||||
|
self.txs = append(txs, unhandledTxs...) |
||||||
|
|
||||||
|
self.block.SetTransactions(txs) |
||||||
|
self.block.SetReceipts(receipts) |
||||||
|
|
||||||
|
// Accumulate the rewards included for this block
|
||||||
|
stateManager.AccumelateRewards(self.block.State(), self.block, parent) |
||||||
|
|
||||||
|
self.block.State().Update() |
||||||
|
|
||||||
|
minerlogger.Infof("Mining on block. Includes %v transactions", len(self.txs)) |
||||||
|
|
||||||
|
// Find a valid nonce
|
||||||
|
nonce := self.pow.Search(self.block, self.powQuitChan) |
||||||
|
if nonce != nil { |
||||||
|
self.block.Nonce = nonce |
||||||
|
err := self.ethereum.StateManager().Process(self.block) |
||||||
|
if err != nil { |
||||||
|
minerlogger.Infoln(err) |
||||||
|
} else { |
||||||
|
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}) |
||||||
|
} |
||||||
|
|
||||||
|
// Continue mining on the next block
|
||||||
|
self.startMining() |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue