github.com/ethereumproject/go-ethereum@v5.5.2+incompatible/miner/miner.go (about) 1 // Copyright 2014 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 // Package miner implements Ethereum block creation and mining. 18 package miner 19 20 import ( 21 "errors" 22 "math/big" 23 "sync/atomic" 24 25 "github.com/ethereumproject/go-ethereum/common" 26 "github.com/ethereumproject/go-ethereum/core" 27 "github.com/ethereumproject/go-ethereum/core/state" 28 "github.com/ethereumproject/go-ethereum/core/types" 29 "github.com/ethereumproject/go-ethereum/eth/downloader" 30 "github.com/ethereumproject/go-ethereum/event" 31 "github.com/ethereumproject/go-ethereum/logger" 32 "github.com/ethereumproject/go-ethereum/logger/glog" 33 "github.com/ethereumproject/go-ethereum/pow" 34 ) 35 36 // HeaderExtra is a freeform description. 37 var HeaderExtra []byte 38 39 type Miner struct { 40 mux *event.TypeMux 41 42 worker *worker 43 44 MinAcceptedGasPrice *big.Int 45 46 threads int 47 coinbase common.Address 48 mining int32 49 eth core.Backend 50 pow pow.PoW 51 52 canStart int32 // can start indicates whether we can start the mining operation 53 shouldStart int32 // should start indicates whether we should start after sync 54 } 55 56 func New(eth core.Backend, config *core.ChainConfig, mux *event.TypeMux, pow pow.PoW) *Miner { 57 miner := &Miner{eth: eth, mux: mux, pow: pow, worker: newWorker(config, common.Address{}, eth), canStart: 1} 58 go miner.update() 59 60 return miner 61 } 62 63 // update keeps track of the downloader events. Please be aware that this is a one shot type of update loop. 64 // It's entered once and as soon as `Done` or `Failed` has been broadcasted the events are unregistered and 65 // the loop is exited. This to prevent a major security vuln where external parties can DOS you with blocks 66 // and halt your mining operation for as long as the DOS continues. 67 func (self *Miner) update() { 68 events := self.mux.Subscribe(downloader.StartEvent{}, downloader.DoneEvent{}, downloader.FailedEvent{}) 69 out: 70 for ev := range events.Chan() { 71 switch ev.Data.(type) { 72 case downloader.StartEvent: 73 atomic.StoreInt32(&self.canStart, 0) 74 if self.Mining() { 75 self.Stop() 76 atomic.StoreInt32(&self.shouldStart, 1) 77 glog.V(logger.Info).Infoln("Mining operation aborted due to sync operation") 78 } 79 case downloader.DoneEvent, downloader.FailedEvent: 80 shouldStart := atomic.LoadInt32(&self.shouldStart) == 1 81 82 atomic.StoreInt32(&self.canStart, 1) 83 atomic.StoreInt32(&self.shouldStart, 0) 84 if shouldStart { 85 self.Start(self.coinbase, self.threads) 86 } 87 // unsubscribe. we're only interested in this event once 88 events.Unsubscribe() 89 // stop immediately and ignore all further pending events 90 break out 91 } 92 } 93 } 94 95 func (m *Miner) SetGasPrice(price *big.Int) error { 96 97 if price == nil { 98 return nil 99 } 100 101 if m.MinAcceptedGasPrice != nil && price.Cmp(m.MinAcceptedGasPrice) == -1 { 102 priceTooLowError := errors.New("Gas price lower than minimum allowed.") 103 return priceTooLowError 104 } 105 106 m.worker.setGasPrice(price) 107 108 return nil 109 } 110 111 func (self *Miner) Start(coinbase common.Address, threads int) { 112 atomic.StoreInt32(&self.shouldStart, 1) 113 self.threads = threads 114 self.worker.coinbase = coinbase 115 self.coinbase = coinbase 116 117 if atomic.LoadInt32(&self.canStart) == 0 { 118 glog.V(logger.Info).Infoln("Can not start mining operation due to network sync (starts when finished)") 119 return 120 } 121 122 atomic.StoreInt32(&self.mining, 1) 123 124 for i := 0; i < threads; i++ { 125 self.worker.register(NewCpuAgent(i, self.pow)) 126 } 127 128 mlogMinerStart.AssignDetails( 129 coinbase.Hex(), 130 threads, 131 ).Send(mlogMiner) 132 glog.V(logger.Info).Infof("Starting mining operation (CPU=%d TOT=%d)\n", threads, len(self.worker.agents)) 133 134 self.worker.start() 135 136 self.worker.commitNewWork() 137 } 138 139 func (self *Miner) Stop() { 140 self.worker.stop() 141 atomic.StoreInt32(&self.mining, 0) 142 atomic.StoreInt32(&self.shouldStart, 0) 143 if logger.MlogEnabled() { 144 mlogMinerStop.AssignDetails( 145 self.coinbase.Hex(), 146 self.threads, 147 ).Send(mlogMiner) 148 } 149 } 150 151 func (self *Miner) Register(agent Agent) { 152 if self.Mining() { 153 agent.Start() 154 } 155 self.worker.register(agent) 156 } 157 158 func (self *Miner) Unregister(agent Agent) { 159 self.worker.unregister(agent) 160 } 161 162 func (self *Miner) Mining() bool { 163 return atomic.LoadInt32(&self.mining) > 0 164 } 165 166 func (self *Miner) HashRate() (tot int64) { 167 tot += self.pow.GetHashrate() 168 // do we care this might race? is it worth we're rewriting some 169 // aspects of the worker/locking up agents so we can get an accurate 170 // hashrate? 171 for agent := range self.worker.agents { 172 tot += agent.GetHashRate() 173 } 174 return 175 } 176 177 // Pending returns the currently pending block and associated state. 178 func (self *Miner) Pending() (*types.Block, *state.StateDB) { 179 return self.worker.pending() 180 } 181 182 func (self *Miner) SetEtherbase(addr common.Address) { 183 self.coinbase = addr 184 self.worker.setEtherbase(addr) 185 }