github.com/klaytn/klaytn@v1.12.1/work/work.go (about) 1 // Modifications Copyright 2018 The klaytn Authors 2 // Copyright 2014 The go-ethereum Authors 3 // This file is part of the go-ethereum library. 4 // 5 // The go-ethereum library is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU Lesser General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // The go-ethereum library is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Lesser General Public License for more details. 14 // 15 // You should have received a copy of the GNU Lesser General Public License 16 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 17 // 18 // This file is derived from miner/miner.go (2018/06/04). 19 // Modified and improved for the klaytn development. 20 21 package work 22 23 import ( 24 "fmt" 25 "io" 26 "math/big" 27 "sync/atomic" 28 29 "github.com/klaytn/klaytn/accounts" 30 "github.com/klaytn/klaytn/blockchain" 31 "github.com/klaytn/klaytn/blockchain/state" 32 "github.com/klaytn/klaytn/blockchain/types" 33 "github.com/klaytn/klaytn/blockchain/vm" 34 "github.com/klaytn/klaytn/common" 35 "github.com/klaytn/klaytn/consensus" 36 "github.com/klaytn/klaytn/datasync/downloader" 37 "github.com/klaytn/klaytn/event" 38 "github.com/klaytn/klaytn/log" 39 "github.com/klaytn/klaytn/params" 40 "github.com/klaytn/klaytn/rlp" 41 "github.com/klaytn/klaytn/snapshot" 42 "github.com/klaytn/klaytn/storage/database" 43 ) 44 45 var logger = log.NewModuleLogger(log.Work) 46 47 //go:generate mockgen -destination=work/mocks/txpool_mock.go -package=mocks github.com/klaytn/klaytn/work TxPool 48 // TxPool is an interface of blockchain.TxPool used by ProtocolManager and Backend. 49 type TxPool interface { 50 // HandleTxMsg should add the given transactions to the pool. 51 HandleTxMsg(types.Transactions) 52 53 // Pending should return pending transactions. 54 // The slice should be modifiable by the caller. 55 Pending() (map[common.Address]types.Transactions, error) 56 57 CachedPendingTxsByCount(count int) types.Transactions 58 59 // SubscribeNewTxsEvent should return an event subscription of 60 // NewTxsEvent and send events to the given channel. 61 SubscribeNewTxsEvent(chan<- blockchain.NewTxsEvent) event.Subscription 62 63 GetPendingNonce(addr common.Address) uint64 64 AddLocal(tx *types.Transaction) error 65 GasPrice() *big.Int 66 SetGasPrice(price *big.Int) 67 Stop() 68 Get(hash common.Hash) *types.Transaction 69 Stats() (int, int) 70 Content() (map[common.Address]types.Transactions, map[common.Address]types.Transactions) 71 StartSpamThrottler(conf *blockchain.ThrottlerConfig) error 72 StopSpamThrottler() 73 } 74 75 // Backend wraps all methods required for mining. 76 type Backend interface { 77 AccountManager() accounts.AccountManager 78 BlockChain() BlockChain 79 TxPool() TxPool 80 ChainDB() database.DBManager 81 ReBroadcastTxs(transactions types.Transactions) 82 } 83 84 // Miner creates blocks and searches for proof-of-work values. 85 type Miner struct { 86 mux *event.TypeMux 87 88 worker *worker 89 90 rewardbase common.Address 91 mining int32 92 backend Backend 93 engine consensus.Engine 94 95 canStart int32 // can start indicates whether we can start the mining operation 96 shouldStart int32 // should start indicates whether we should start after sync 97 } 98 99 func New(backend Backend, config *params.ChainConfig, mux *event.TypeMux, engine consensus.Engine, nodetype common.ConnType, rewardbase common.Address, TxResendUseLegacy bool) *Miner { 100 miner := &Miner{ 101 backend: backend, 102 mux: mux, 103 engine: engine, 104 worker: newWorker(config, engine, rewardbase, backend, mux, nodetype, TxResendUseLegacy), 105 canStart: 1, 106 } 107 // TODO-Klaytn drop or missing tx 108 miner.Register(NewCpuAgent(backend.BlockChain(), engine, nodetype)) 109 go miner.update() 110 111 return miner 112 } 113 114 // update keeps track of the downloader events. Please be aware that this is a one shot type of update loop. 115 // It's entered once and as soon as `Done` or `Failed` has been broadcasted the events are unregistered and 116 // the loop is exited. This to prevent a major security vuln where external parties can DOS you with blocks 117 // and halt your mining operation for as long as the DOS continues. 118 func (self *Miner) update() { 119 events := self.mux.Subscribe(downloader.StartEvent{}, downloader.DoneEvent{}, downloader.FailedEvent{}) 120 out: 121 for ev := range events.Chan() { 122 switch ev.Data.(type) { 123 case downloader.StartEvent: 124 atomic.StoreInt32(&self.canStart, 0) 125 if self.Mining() { 126 self.Stop() 127 atomic.StoreInt32(&self.shouldStart, 1) 128 logger.Info("Mining aborted due to sync") 129 } 130 case downloader.DoneEvent, downloader.FailedEvent: 131 shouldStart := atomic.LoadInt32(&self.shouldStart) == 1 132 133 atomic.StoreInt32(&self.canStart, 1) 134 atomic.StoreInt32(&self.shouldStart, 0) 135 if shouldStart { 136 self.Start() 137 } 138 // unsubscribe. we're only interested in this event once 139 events.Unsubscribe() 140 // stop immediately and ignore all further pending events 141 break out 142 } 143 } 144 } 145 146 func (self *Miner) Start() { 147 atomic.StoreInt32(&self.shouldStart, 1) 148 149 if atomic.LoadInt32(&self.canStart) == 0 { 150 logger.Info("Network syncing, will start work afterwards") 151 return 152 } 153 atomic.StoreInt32(&self.mining, 1) 154 155 if self.worker.nodetype == common.CONSENSUSNODE { 156 logger.Info("Starting mining operation") 157 } 158 self.worker.start() 159 self.worker.commitNewWork() 160 } 161 162 func (self *Miner) Stop() { 163 self.worker.stop() 164 atomic.StoreInt32(&self.mining, 0) 165 atomic.StoreInt32(&self.shouldStart, 0) 166 } 167 168 func (self *Miner) Register(agent Agent) { 169 if self.Mining() { 170 agent.Start() 171 } 172 self.worker.register(agent) 173 } 174 175 func (self *Miner) Unregister(agent Agent) { 176 self.worker.unregister(agent) 177 } 178 179 func (self *Miner) Mining() bool { 180 return atomic.LoadInt32(&self.mining) > 0 181 } 182 183 func (self *Miner) HashRate() (tot int64) { 184 if pow, ok := self.engine.(consensus.PoW); ok { 185 tot += int64(pow.Hashrate()) 186 } 187 // do we care this might race? is it worth we're rewriting some 188 // aspects of the worker/locking up agents so we can get an accurate 189 // hashrate? 190 for agent := range self.worker.agents { 191 if _, ok := agent.(*CpuAgent); !ok { 192 tot += agent.GetHashRate() 193 } 194 } 195 return 196 } 197 198 func (self *Miner) SetExtra(extra []byte) error { 199 // istanbul BFT 200 maximumExtraDataSize := params.GetMaximumExtraDataSize() 201 if uint64(len(extra)) > maximumExtraDataSize { 202 return fmt.Errorf("Extra exceeds max length. %d > %v", len(extra), maximumExtraDataSize) 203 } 204 self.worker.setExtra(extra) 205 return nil 206 } 207 208 // Pending returns the currently pending block and associated state. 209 func (self *Miner) Pending() (*types.Block, *state.StateDB) { 210 return self.worker.pending() 211 } 212 213 // PendingBlock returns the currently pending block. 214 // 215 // Note, to access both the pending block and the pending state 216 // simultaneously, please use Pending(), as the pending state can 217 // change between multiple method calls 218 func (self *Miner) PendingBlock() *types.Block { 219 return self.worker.pendingBlock() 220 } 221 222 //go:generate mockgen -destination=mocks/blockchain_mock.go -package=mocks github.com/klaytn/klaytn/work BlockChain 223 // BlockChain is an interface of blockchain.BlockChain used by ProtocolManager. 224 type BlockChain interface { 225 Genesis() *types.Block 226 227 CurrentBlock() *types.Block 228 CurrentFastBlock() *types.Block 229 HasBlock(hash common.Hash, number uint64) bool 230 GetBlock(hash common.Hash, number uint64) *types.Block 231 GetBlockByHash(hash common.Hash) *types.Block 232 GetBlockByNumber(number uint64) *types.Block 233 GetBlockHashesFromHash(hash common.Hash, max uint64) []common.Hash 234 235 CurrentHeader() *types.Header 236 HasHeader(hash common.Hash, number uint64) bool 237 GetHeader(hash common.Hash, number uint64) *types.Header 238 GetHeaderByHash(hash common.Hash) *types.Header 239 GetHeaderByNumber(number uint64) *types.Header 240 241 GetTd(hash common.Hash, number uint64) *big.Int 242 GetTdByHash(hash common.Hash) *big.Int 243 244 GetBodyRLP(hash common.Hash) rlp.RawValue 245 246 GetReceiptsByBlockHash(blockHash common.Hash) types.Receipts 247 248 InsertChain(chain types.Blocks) (int, error) 249 TrieNode(hash common.Hash) ([]byte, error) 250 ContractCode(hash common.Hash) ([]byte, error) 251 ContractCodeWithPrefix(hash common.Hash) ([]byte, error) 252 Config() *params.ChainConfig 253 State() (*state.StateDB, error) 254 Rollback(chain []common.Hash) 255 InsertReceiptChain(blockChain types.Blocks, receiptChain []types.Receipts) (int, error) 256 InsertHeaderChain(chain []*types.Header, checkFreq int) (int, error) 257 FastSyncCommitHead(hash common.Hash) error 258 StateCache() state.Database 259 260 SubscribeChainEvent(ch chan<- blockchain.ChainEvent) event.Subscription 261 SetHead(head uint64) error 262 Stop() 263 264 SubscribeRemovedLogsEvent(ch chan<- blockchain.RemovedLogsEvent) event.Subscription 265 SubscribeChainHeadEvent(ch chan<- blockchain.ChainHeadEvent) event.Subscription 266 SubscribeChainSideEvent(ch chan<- blockchain.ChainSideEvent) event.Subscription 267 SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscription 268 IsParallelDBWrite() bool 269 IsSenderTxHashIndexingEnabled() bool 270 271 Processor() blockchain.Processor 272 BadBlocks() ([]blockchain.BadBlockArgs, error) 273 StateAt(root common.Hash) (*state.StateDB, error) 274 PrunableStateAt(root common.Hash, num uint64) (*state.StateDB, error) 275 StateAtWithPersistent(root common.Hash) (*state.StateDB, error) 276 StateAtWithGCLock(root common.Hash) (*state.StateDB, error) 277 Export(w io.Writer) error 278 ExportN(w io.Writer, first, last uint64) error 279 Engine() consensus.Engine 280 GetTxLookupInfoAndReceipt(txHash common.Hash) (*types.Transaction, common.Hash, uint64, uint64, *types.Receipt) 281 GetTxAndLookupInfoInCache(hash common.Hash) (*types.Transaction, common.Hash, uint64, uint64) 282 GetBlockReceiptsInCache(blockHash common.Hash) types.Receipts 283 GetTxLookupInfoAndReceiptInCache(txHash common.Hash) (*types.Transaction, common.Hash, uint64, uint64, *types.Receipt) 284 GetTxAndLookupInfo(txHash common.Hash) (*types.Transaction, common.Hash, uint64, uint64) 285 GetLogsByHash(hash common.Hash) [][]*types.Log 286 ResetWithGenesisBlock(gb *types.Block) error 287 Validator() blockchain.Validator 288 HasBadBlock(hash common.Hash) bool 289 WriteBlockWithState(block *types.Block, receipts []*types.Receipt, stateDB *state.StateDB) (blockchain.WriteResult, error) 290 PostChainEvents(events []interface{}, logs []*types.Log) 291 ApplyTransaction(config *params.ChainConfig, author *common.Address, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *uint64, cfg *vm.Config) (*types.Receipt, *vm.InternalTxTrace, error) 292 293 // State Migration 294 PrepareStateMigration() error 295 StartStateMigration(uint64, common.Hash) error 296 StopStateMigration() error 297 StateMigrationStatus() (bool, uint64, int, int, int, float64, error) 298 299 // Warm up 300 StartWarmUp(minLoad uint) error 301 StartContractWarmUp(contractAddr common.Address, minLoad uint) error 302 StopWarmUp() error 303 304 // Collect state/storage trie statistics 305 StartCollectingTrieStats(contractAddr common.Address) error 306 GetContractStorageRoot(block *types.Block, db state.Database, contractAddr common.Address) (common.ExtHash, error) 307 308 // Save trie node cache to this 309 SaveTrieNodeCacheToDisk() error 310 311 // KES 312 BlockSubscriptionLoop(pool *blockchain.TxPool) 313 CloseBlockSubscriptionLoop() 314 315 // read-only mode 316 CurrentBlockUpdateLoop(pool *blockchain.TxPool) 317 318 // Snapshot 319 Snapshots() *snapshot.Tree 320 }