package miner import ( "math/big" "sync/atomic" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/eth/downloader" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger/glog" "github.com/ethereum/go-ethereum/pow" ) type Miner struct { mux *event.TypeMux worker *worker MinAcceptedGasPrice *big.Int threads int coinbase common.Address mining int32 eth core.Backend pow pow.PoW canStart int32 // can start indicates whether we can start the mining operation shouldStart int32 // should start indicates whether we should start after sync } func New(eth core.Backend, mux *event.TypeMux, pow pow.PoW) *Miner { miner := &Miner{eth: eth, mux: mux, pow: pow, worker: newWorker(common.Address{}, eth), canStart: 1} go miner.update() return miner } // update keeps track of the downloader events. Please be aware that this is a one shot type of update loop. // It's entered once and as soon as `Done` or `Failed` has been broadcasted the events are unregistered and // the loop is exited. This to prevent a major security vuln where external parties can DOS you with blocks // and halt your mining operation for as long as the DOS continues. func (self *Miner) update() { events := self.mux.Subscribe(downloader.StartEvent{}, downloader.DoneEvent{}, downloader.FailedEvent{}) for ev := range events.Chan() { switch ev.(type) { case downloader.StartEvent: atomic.StoreInt32(&self.canStart, 0) if self.Mining() { self.Stop() atomic.StoreInt32(&self.shouldStart, 1) glog.V(logger.Info).Infoln("Mining operation aborted due to sync operation") } case downloader.DoneEvent, downloader.FailedEvent: shouldStart := atomic.LoadInt32(&self.shouldStart) == 1 atomic.StoreInt32(&self.canStart, 1) atomic.StoreInt32(&self.shouldStart, 0) if shouldStart { self.Start(self.coinbase, self.threads) } } // unsubscribe. we're only interested in this event once events.Unsubscribe() // stop immediately and ignore all further pending events break } } func (m *Miner) SetGasPrice(price *big.Int) { // FIXME block tests set a nil gas price. Quick dirty fix if price == nil { return } m.worker.gasPrice = price } func (self *Miner) Start(coinbase common.Address, threads int) { atomic.StoreInt32(&self.shouldStart, 1) self.threads = threads self.worker.coinbase = coinbase self.coinbase = coinbase if atomic.LoadInt32(&self.canStart) == 0 { glog.V(logger.Info).Infoln("Can not start mining operation due to network sync (starts when finished)") return } atomic.StoreInt32(&self.mining, 1) for i := 0; i < threads; i++ { self.worker.register(NewCpuAgent(i, self.pow)) } glog.V(logger.Info).Infof("Starting mining operation (CPU=%d TOT=%d)\n", threads, len(self.worker.agents)) self.worker.start() self.worker.commitNewWork() } func (self *Miner) Stop() { self.worker.stop() atomic.StoreInt32(&self.mining, 0) atomic.StoreInt32(&self.shouldStart, 0) } func (self *Miner) Register(agent Agent) { if self.Mining() { agent.Start() } self.worker.register(agent) } func (self *Miner) Mining() bool { return atomic.LoadInt32(&self.mining) > 0 } func (self *Miner) HashRate() int64 { return self.pow.GetHashrate() } func (self *Miner) SetExtra(extra []byte) { self.worker.extra = extra } func (self *Miner) PendingState() *state.StateDB { return self.worker.pendingState() } func (self *Miner) PendingBlock() *types.Block { return self.worker.pendingBlock() }