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 }