github.com/sberex/go-sberex@v1.8.2-0.20181113200658-ed96ac38f7d7/miner/miner.go (about)

     1  // This file is part of the go-sberex library. The go-sberex library is 
     2  // free software: you can redistribute it and/or modify it under the terms 
     3  // of the GNU Lesser General Public License as published by the Free 
     4  // Software Foundation, either version 3 of the License, or (at your option)
     5  // any later version.
     6  //
     7  // The go-sberex library is distributed in the hope that it will be useful, 
     8  // but WITHOUT ANY WARRANTY; without even the implied warranty of
     9  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 
    10  // General Public License <http://www.gnu.org/licenses/> for more details.
    11  
    12  // Package miner implements Sberex block creation and mining.
    13  package miner
    14  
    15  import (
    16  	"fmt"
    17  	"sync/atomic"
    18  
    19  	"github.com/Sberex/go-sberex/accounts"
    20  	"github.com/Sberex/go-sberex/common"
    21  	"github.com/Sberex/go-sberex/consensus"
    22  	"github.com/Sberex/go-sberex/core"
    23  	"github.com/Sberex/go-sberex/core/state"
    24  	"github.com/Sberex/go-sberex/core/types"
    25  	"github.com/Sberex/go-sberex/eth/downloader"
    26  	"github.com/Sberex/go-sberex/ethdb"
    27  	"github.com/Sberex/go-sberex/event"
    28  	"github.com/Sberex/go-sberex/log"
    29  	"github.com/Sberex/go-sberex/params"
    30  )
    31  
    32  // Backend wraps all methods required for mining.
    33  type Backend interface {
    34  	AccountManager() *accounts.Manager
    35  	BlockChain() *core.BlockChain
    36  	TxPool() *core.TxPool
    37  	ChainDb() ethdb.Database
    38  }
    39  
    40  // Miner creates blocks and searches for proof-of-work values.
    41  type Miner struct {
    42  	mux *event.TypeMux
    43  
    44  	worker *worker
    45  
    46  	coinbase common.Address
    47  	mining   int32
    48  	eth      Backend
    49  	engine   consensus.Engine
    50  
    51  	canStart    int32 // can start indicates whether we can start the mining operation
    52  	shouldStart int32 // should start indicates whether we should start after sync
    53  }
    54  
    55  func New(eth Backend, config *params.ChainConfig, mux *event.TypeMux, engine consensus.Engine) *Miner {
    56  	miner := &Miner{
    57  		eth:      eth,
    58  		mux:      mux,
    59  		engine:   engine,
    60  		worker:   newWorker(config, engine, common.Address{}, eth, mux),
    61  		canStart: 1,
    62  	}
    63  	miner.Register(NewCpuAgent(eth.BlockChain(), engine))
    64  	go miner.update()
    65  
    66  	return miner
    67  }
    68  
    69  // update keeps track of the downloader events. Please be aware that this is a one shot type of update loop.
    70  // It's entered once and as soon as `Done` or `Failed` has been broadcasted the events are unregistered and
    71  // the loop is exited. This to prevent a major security vuln where external parties can DOS you with blocks
    72  // and halt your mining operation for as long as the DOS continues.
    73  func (self *Miner) update() {
    74  	events := self.mux.Subscribe(downloader.StartEvent{}, downloader.DoneEvent{}, downloader.FailedEvent{})
    75  out:
    76  	for ev := range events.Chan() {
    77  		switch ev.Data.(type) {
    78  		case downloader.StartEvent:
    79  			atomic.StoreInt32(&self.canStart, 0)
    80  			if self.Mining() {
    81  				self.Stop()
    82  				atomic.StoreInt32(&self.shouldStart, 1)
    83  				log.Info("Mining aborted due to sync")
    84  			}
    85  		case downloader.DoneEvent, downloader.FailedEvent:
    86  			shouldStart := atomic.LoadInt32(&self.shouldStart) == 1
    87  
    88  			atomic.StoreInt32(&self.canStart, 1)
    89  			atomic.StoreInt32(&self.shouldStart, 0)
    90  			if shouldStart {
    91  				self.Start(self.coinbase)
    92  			}
    93  			// unsubscribe. we're only interested in this event once
    94  			events.Unsubscribe()
    95  			// stop immediately and ignore all further pending events
    96  			break out
    97  		}
    98  	}
    99  }
   100  
   101  func (self *Miner) Start(coinbase common.Address) {
   102  	atomic.StoreInt32(&self.shouldStart, 1)
   103  	self.worker.setEtherbase(coinbase)
   104  	self.coinbase = coinbase
   105  
   106  	if atomic.LoadInt32(&self.canStart) == 0 {
   107  		log.Info("Network syncing, will start miner afterwards")
   108  		return
   109  	}
   110  	atomic.StoreInt32(&self.mining, 1)
   111  
   112  	log.Info("Starting mining operation")
   113  	self.worker.start()
   114  	self.worker.commitNewWork()
   115  }
   116  
   117  func (self *Miner) Stop() {
   118  	self.worker.stop()
   119  	atomic.StoreInt32(&self.mining, 0)
   120  	atomic.StoreInt32(&self.shouldStart, 0)
   121  }
   122  
   123  func (self *Miner) Register(agent Agent) {
   124  	if self.Mining() {
   125  		agent.Start()
   126  	}
   127  	self.worker.register(agent)
   128  }
   129  
   130  func (self *Miner) Unregister(agent Agent) {
   131  	self.worker.unregister(agent)
   132  }
   133  
   134  func (self *Miner) Mining() bool {
   135  	return atomic.LoadInt32(&self.mining) > 0
   136  }
   137  
   138  func (self *Miner) HashRate() (tot int64) {
   139  	if pow, ok := self.engine.(consensus.PoW); ok {
   140  		tot += int64(pow.Hashrate())
   141  	}
   142  	// do we care this might race? is it worth we're rewriting some
   143  	// aspects of the worker/locking up agents so we can get an accurate
   144  	// hashrate?
   145  	for agent := range self.worker.agents {
   146  		if _, ok := agent.(*CpuAgent); !ok {
   147  			tot += agent.GetHashRate()
   148  		}
   149  	}
   150  	return
   151  }
   152  
   153  func (self *Miner) SetExtra(extra []byte) error {
   154  	if uint64(len(extra)) > params.MaximumExtraDataSize {
   155  		return fmt.Errorf("Extra exceeds max length. %d > %v", len(extra), params.MaximumExtraDataSize)
   156  	}
   157  	self.worker.setExtra(extra)
   158  	return nil
   159  }
   160  
   161  // Pending returns the currently pending block and associated state.
   162  func (self *Miner) Pending() (*types.Block, *state.StateDB) {
   163  	return self.worker.pending()
   164  }
   165  
   166  // PendingBlock returns the currently pending block.
   167  //
   168  // Note, to access both the pending block and the pending state
   169  // simultaneously, please use Pending(), as the pending state can
   170  // change between multiple method calls
   171  func (self *Miner) PendingBlock() *types.Block {
   172  	return self.worker.pendingBlock()
   173  }
   174  
   175  func (self *Miner) SetEtherbase(addr common.Address) {
   176  	self.coinbase = addr
   177  	self.worker.setEtherbase(addr)
   178  }