github.com/sberex/go-sberex@v1.8.2-0.20181113200658-ed96ac38f7d7/miner/worker.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 13 14 import ( 15 "bytes" 16 "fmt" 17 "math/big" 18 "sync" 19 "sync/atomic" 20 "time" 21 22 "github.com/Sberex/go-sberex/common" 23 "github.com/Sberex/go-sberex/consensus" 24 "github.com/Sberex/go-sberex/consensus/misc" 25 "github.com/Sberex/go-sberex/core" 26 "github.com/Sberex/go-sberex/core/state" 27 "github.com/Sberex/go-sberex/core/types" 28 "github.com/Sberex/go-sberex/core/vm" 29 "github.com/Sberex/go-sberex/ethdb" 30 "github.com/Sberex/go-sberex/event" 31 "github.com/Sberex/go-sberex/log" 32 "github.com/Sberex/go-sberex/params" 33 "gopkg.in/fatih/set.v0" 34 ) 35 36 const ( 37 resultQueueSize = 10 38 miningLogAtDepth = 5 39 40 // txChanSize is the size of channel listening to TxPreEvent. 41 // The number is referenced from the size of tx pool. 42 txChanSize = 4096 43 // chainHeadChanSize is the size of channel listening to ChainHeadEvent. 44 chainHeadChanSize = 10 45 // chainSideChanSize is the size of channel listening to ChainSideEvent. 46 chainSideChanSize = 10 47 ) 48 49 // Agent can register themself with the worker 50 type Agent interface { 51 Work() chan<- *Work 52 SetReturnCh(chan<- *Result) 53 Stop() 54 Start() 55 GetHashRate() int64 56 } 57 58 // Work is the workers current environment and holds 59 // all of the current state information 60 type Work struct { 61 config *params.ChainConfig 62 signer types.Signer 63 64 state *state.StateDB // apply state changes here 65 ancestors *set.Set // ancestor set (used for checking uncle parent validity) 66 family *set.Set // family set (used for checking uncle invalidity) 67 uncles *set.Set // uncle set 68 tcount int // tx count in cycle 69 70 Block *types.Block // the new block 71 72 header *types.Header 73 txs []*types.Transaction 74 receipts []*types.Receipt 75 76 createdAt time.Time 77 } 78 79 type Result struct { 80 Work *Work 81 Block *types.Block 82 } 83 84 // worker is the main object which takes care of applying messages to the new state 85 type worker struct { 86 config *params.ChainConfig 87 engine consensus.Engine 88 89 mu sync.Mutex 90 91 // update loop 92 mux *event.TypeMux 93 txCh chan core.TxPreEvent 94 txSub event.Subscription 95 chainHeadCh chan core.ChainHeadEvent 96 chainHeadSub event.Subscription 97 chainSideCh chan core.ChainSideEvent 98 chainSideSub event.Subscription 99 wg sync.WaitGroup 100 101 agents map[Agent]struct{} 102 recv chan *Result 103 104 eth Backend 105 chain *core.BlockChain 106 proc core.Validator 107 chainDb ethdb.Database 108 109 coinbase common.Address 110 extra []byte 111 112 currentMu sync.Mutex 113 current *Work 114 115 uncleMu sync.Mutex 116 possibleUncles map[common.Hash]*types.Block 117 118 unconfirmed *unconfirmedBlocks // set of locally mined blocks pending canonicalness confirmations 119 120 // atomic status counters 121 mining int32 122 atWork int32 123 } 124 125 func newWorker(config *params.ChainConfig, engine consensus.Engine, coinbase common.Address, eth Backend, mux *event.TypeMux) *worker { 126 worker := &worker{ 127 config: config, 128 engine: engine, 129 eth: eth, 130 mux: mux, 131 txCh: make(chan core.TxPreEvent, txChanSize), 132 chainHeadCh: make(chan core.ChainHeadEvent, chainHeadChanSize), 133 chainSideCh: make(chan core.ChainSideEvent, chainSideChanSize), 134 chainDb: eth.ChainDb(), 135 recv: make(chan *Result, resultQueueSize), 136 chain: eth.BlockChain(), 137 proc: eth.BlockChain().Validator(), 138 possibleUncles: make(map[common.Hash]*types.Block), 139 coinbase: coinbase, 140 agents: make(map[Agent]struct{}), 141 unconfirmed: newUnconfirmedBlocks(eth.BlockChain(), miningLogAtDepth), 142 } 143 // Subscribe TxPreEvent for tx pool 144 worker.txSub = eth.TxPool().SubscribeTxPreEvent(worker.txCh) 145 // Subscribe events for blockchain 146 worker.chainHeadSub = eth.BlockChain().SubscribeChainHeadEvent(worker.chainHeadCh) 147 worker.chainSideSub = eth.BlockChain().SubscribeChainSideEvent(worker.chainSideCh) 148 go worker.update() 149 150 go worker.wait() 151 worker.commitNewWork() 152 153 return worker 154 } 155 156 func (self *worker) setEtherbase(addr common.Address) { 157 self.mu.Lock() 158 defer self.mu.Unlock() 159 self.coinbase = addr 160 } 161 162 func (self *worker) setExtra(extra []byte) { 163 self.mu.Lock() 164 defer self.mu.Unlock() 165 self.extra = extra 166 } 167 168 func (self *worker) pending() (*types.Block, *state.StateDB) { 169 self.currentMu.Lock() 170 defer self.currentMu.Unlock() 171 172 if atomic.LoadInt32(&self.mining) == 0 { 173 return types.NewBlock( 174 self.current.header, 175 self.current.txs, 176 nil, 177 self.current.receipts, 178 ), self.current.state.Copy() 179 } 180 return self.current.Block, self.current.state.Copy() 181 } 182 183 func (self *worker) pendingBlock() *types.Block { 184 self.currentMu.Lock() 185 defer self.currentMu.Unlock() 186 187 if atomic.LoadInt32(&self.mining) == 0 { 188 return types.NewBlock( 189 self.current.header, 190 self.current.txs, 191 nil, 192 self.current.receipts, 193 ) 194 } 195 return self.current.Block 196 } 197 198 func (self *worker) start() { 199 self.mu.Lock() 200 defer self.mu.Unlock() 201 202 atomic.StoreInt32(&self.mining, 1) 203 204 // spin up agents 205 for agent := range self.agents { 206 agent.Start() 207 } 208 } 209 210 func (self *worker) stop() { 211 self.wg.Wait() 212 213 self.mu.Lock() 214 defer self.mu.Unlock() 215 if atomic.LoadInt32(&self.mining) == 1 { 216 for agent := range self.agents { 217 agent.Stop() 218 } 219 } 220 atomic.StoreInt32(&self.mining, 0) 221 atomic.StoreInt32(&self.atWork, 0) 222 } 223 224 func (self *worker) register(agent Agent) { 225 self.mu.Lock() 226 defer self.mu.Unlock() 227 self.agents[agent] = struct{}{} 228 agent.SetReturnCh(self.recv) 229 } 230 231 func (self *worker) unregister(agent Agent) { 232 self.mu.Lock() 233 defer self.mu.Unlock() 234 delete(self.agents, agent) 235 agent.Stop() 236 } 237 238 func (self *worker) update() { 239 defer self.txSub.Unsubscribe() 240 defer self.chainHeadSub.Unsubscribe() 241 defer self.chainSideSub.Unsubscribe() 242 243 for { 244 // A real event arrived, process interesting content 245 select { 246 // Handle ChainHeadEvent 247 case <-self.chainHeadCh: 248 self.commitNewWork() 249 250 // Handle ChainSideEvent 251 case ev := <-self.chainSideCh: 252 self.uncleMu.Lock() 253 self.possibleUncles[ev.Block.Hash()] = ev.Block 254 self.uncleMu.Unlock() 255 256 // Handle TxPreEvent 257 case ev := <-self.txCh: 258 // Apply transaction to the pending state if we're not mining 259 if atomic.LoadInt32(&self.mining) == 0 { 260 self.currentMu.Lock() 261 acc, _ := types.Sender(self.current.signer, ev.Tx) 262 txs := map[common.Address]types.Transactions{acc: {ev.Tx}} 263 txset := types.NewTransactionsByPriceAndNonce(self.current.signer, txs) 264 265 self.current.commitTransactions(self.mux, txset, self.chain, self.coinbase) 266 self.currentMu.Unlock() 267 } else { 268 // If we're mining, but nothing is being processed, wake on new transactions 269 if self.config.Clique != nil && self.config.Clique.Period == 0 { 270 self.commitNewWork() 271 } 272 } 273 274 // System stopped 275 case <-self.txSub.Err(): 276 return 277 case <-self.chainHeadSub.Err(): 278 return 279 case <-self.chainSideSub.Err(): 280 return 281 } 282 } 283 } 284 285 func (self *worker) wait() { 286 for { 287 mustCommitNewWork := true 288 for result := range self.recv { 289 atomic.AddInt32(&self.atWork, -1) 290 291 if result == nil { 292 continue 293 } 294 block := result.Block 295 work := result.Work 296 297 // Update the block hash in all logs since it is now available and not when the 298 // receipt/log of individual transactions were created. 299 for _, r := range work.receipts { 300 for _, l := range r.Logs { 301 l.BlockHash = block.Hash() 302 } 303 } 304 for _, log := range work.state.Logs() { 305 log.BlockHash = block.Hash() 306 } 307 stat, err := self.chain.WriteBlockWithState(block, work.receipts, work.state) 308 if err != nil { 309 log.Error("Failed writing block to chain", "err", err) 310 continue 311 } 312 // check if canon block and write transactions 313 if stat == core.CanonStatTy { 314 // implicit by posting ChainHeadEvent 315 mustCommitNewWork = false 316 } 317 // Broadcast the block and announce chain insertion event 318 self.mux.Post(core.NewMinedBlockEvent{Block: block}) 319 var ( 320 events []interface{} 321 logs = work.state.Logs() 322 ) 323 events = append(events, core.ChainEvent{Block: block, Hash: block.Hash(), Logs: logs}) 324 if stat == core.CanonStatTy { 325 events = append(events, core.ChainHeadEvent{Block: block}) 326 } 327 self.chain.PostChainEvents(events, logs) 328 329 // Insert the block into the set of pending ones to wait for confirmations 330 self.unconfirmed.Insert(block.NumberU64(), block.Hash()) 331 332 if mustCommitNewWork { 333 self.commitNewWork() 334 } 335 } 336 } 337 } 338 339 // push sends a new work task to currently live miner agents. 340 func (self *worker) push(work *Work) { 341 if atomic.LoadInt32(&self.mining) != 1 { 342 return 343 } 344 for agent := range self.agents { 345 atomic.AddInt32(&self.atWork, 1) 346 if ch := agent.Work(); ch != nil { 347 ch <- work 348 } 349 } 350 } 351 352 // makeCurrent creates a new environment for the current cycle. 353 func (self *worker) makeCurrent(parent *types.Block, header *types.Header) error { 354 state, err := self.chain.StateAt(parent.Root()) 355 if err != nil { 356 return err 357 } 358 work := &Work{ 359 config: self.config, 360 signer: types.NewEIP155Signer(self.config.ChainId), 361 state: state, 362 ancestors: set.New(), 363 family: set.New(), 364 uncles: set.New(), 365 header: header, 366 createdAt: time.Now(), 367 } 368 369 // when 08 is processed ancestors contain 07 (quick block) 370 for _, ancestor := range self.chain.GetBlocksFromHash(parent.Hash(), 7) { 371 for _, uncle := range ancestor.Uncles() { 372 work.family.Add(uncle.Hash()) 373 } 374 work.family.Add(ancestor.Hash()) 375 work.ancestors.Add(ancestor.Hash()) 376 } 377 378 // Keep track of transactions which return errors so they can be removed 379 work.tcount = 0 380 self.current = work 381 return nil 382 } 383 384 func (self *worker) commitNewWork() { 385 self.mu.Lock() 386 defer self.mu.Unlock() 387 self.uncleMu.Lock() 388 defer self.uncleMu.Unlock() 389 self.currentMu.Lock() 390 defer self.currentMu.Unlock() 391 392 tstart := time.Now() 393 parent := self.chain.CurrentBlock() 394 395 tstamp := tstart.Unix() 396 if parent.Time().Cmp(new(big.Int).SetInt64(tstamp)) >= 0 { 397 tstamp = parent.Time().Int64() + 1 398 } 399 // this will ensure we're not going off too far in the future 400 if now := time.Now().Unix(); tstamp > now+1 { 401 wait := time.Duration(tstamp-now) * time.Second 402 log.Info("Mining too far in the future", "wait", common.PrettyDuration(wait)) 403 time.Sleep(wait) 404 } 405 406 num := parent.Number() 407 header := &types.Header{ 408 ParentHash: parent.Hash(), 409 Number: num.Add(num, common.Big1), 410 GasLimit: core.CalcGasLimit(parent), 411 Extra: self.extra, 412 Time: big.NewInt(tstamp), 413 } 414 // Only set the coinbase if we are mining (avoid spurious block rewards) 415 if atomic.LoadInt32(&self.mining) == 1 { 416 header.Coinbase = self.coinbase 417 } 418 if err := self.engine.Prepare(self.chain, header); err != nil { 419 log.Error("Failed to prepare header for mining", "err", err) 420 return 421 } 422 // If we are care about TheDAO hard-fork check whether to override the extra-data or not 423 if daoBlock := self.config.DAOForkBlock; daoBlock != nil { 424 // Check whether the block is among the fork extra-override range 425 limit := new(big.Int).Add(daoBlock, params.DAOForkExtraRange) 426 if header.Number.Cmp(daoBlock) >= 0 && header.Number.Cmp(limit) < 0 { 427 // Depending whether we support or oppose the fork, override differently 428 if self.config.DAOForkSupport { 429 header.Extra = common.CopyBytes(params.DAOForkBlockExtra) 430 } else if bytes.Equal(header.Extra, params.DAOForkBlockExtra) { 431 header.Extra = []byte{} // If miner opposes, don't let it use the reserved extra-data 432 } 433 } 434 } 435 // Could potentially happen if starting to mine in an odd state. 436 err := self.makeCurrent(parent, header) 437 if err != nil { 438 log.Error("Failed to create mining context", "err", err) 439 return 440 } 441 // Create the current work task and check any fork transitions needed 442 work := self.current 443 if self.config.DAOForkSupport && self.config.DAOForkBlock != nil && self.config.DAOForkBlock.Cmp(header.Number) == 0 { 444 misc.ApplyDAOHardFork(work.state) 445 } 446 pending, err := self.eth.TxPool().Pending() 447 if err != nil { 448 log.Error("Failed to fetch pending transactions", "err", err) 449 return 450 } 451 txs := types.NewTransactionsByPriceAndNonce(self.current.signer, pending) 452 work.commitTransactions(self.mux, txs, self.chain, self.coinbase) 453 454 // compute uncles for the new block. 455 var ( 456 uncles []*types.Header 457 badUncles []common.Hash 458 ) 459 for hash, uncle := range self.possibleUncles { 460 if len(uncles) == 2 { 461 break 462 } 463 if err := self.commitUncle(work, uncle.Header()); err != nil { 464 log.Trace("Bad uncle found and will be removed", "hash", hash) 465 log.Trace(fmt.Sprint(uncle)) 466 467 badUncles = append(badUncles, hash) 468 } else { 469 log.Debug("Committing new uncle to block", "hash", hash) 470 uncles = append(uncles, uncle.Header()) 471 } 472 } 473 for _, hash := range badUncles { 474 delete(self.possibleUncles, hash) 475 } 476 // Create the new block to seal with the consensus engine 477 if work.Block, err = self.engine.Finalize(self.chain, header, work.state, work.txs, uncles, work.receipts); err != nil { 478 log.Error("Failed to finalize block for sealing", "err", err) 479 return 480 } 481 // We only care about logging if we're actually mining. 482 if atomic.LoadInt32(&self.mining) == 1 { 483 log.Info("Commit new mining work", "number", work.Block.Number(), "txs", work.tcount, "uncles", len(uncles), "elapsed", common.PrettyDuration(time.Since(tstart))) 484 self.unconfirmed.Shift(work.Block.NumberU64() - 1) 485 } 486 self.push(work) 487 } 488 489 func (self *worker) commitUncle(work *Work, uncle *types.Header) error { 490 hash := uncle.Hash() 491 if work.uncles.Has(hash) { 492 return fmt.Errorf("uncle not unique") 493 } 494 if !work.ancestors.Has(uncle.ParentHash) { 495 return fmt.Errorf("uncle's parent unknown (%x)", uncle.ParentHash[0:4]) 496 } 497 if work.family.Has(hash) { 498 return fmt.Errorf("uncle already in family (%x)", hash) 499 } 500 work.uncles.Add(uncle.Hash()) 501 return nil 502 } 503 504 func (env *Work) commitTransactions(mux *event.TypeMux, txs *types.TransactionsByPriceAndNonce, bc *core.BlockChain, coinbase common.Address) { 505 gp := new(core.GasPool).AddGas(env.header.GasLimit) 506 507 var coalescedLogs []*types.Log 508 509 for { 510 // If we don't have enough gas for any further transactions then we're done 511 if gp.Gas() < params.TxGas { 512 log.Trace("Not enough gas for further transactions", "gp", gp) 513 break 514 } 515 // Retrieve the next transaction and abort if all done 516 tx := txs.Peek() 517 if tx == nil { 518 break 519 } 520 // Error may be ignored here. The error has already been checked 521 // during transaction acceptance is the transaction pool. 522 // 523 // We use the eip155 signer regardless of the current hf. 524 from, _ := types.Sender(env.signer, tx) 525 // Check whether the tx is replay protected. If we're not in the EIP155 hf 526 // phase, start ignoring the sender until we do. 527 if tx.Protected() && !env.config.IsEIP155(env.header.Number) { 528 log.Trace("Ignoring reply protected transaction", "hash", tx.Hash(), "eip155", env.config.EIP155Block) 529 530 txs.Pop() 531 continue 532 } 533 // Start executing the transaction 534 env.state.Prepare(tx.Hash(), common.Hash{}, env.tcount) 535 536 err, logs := env.commitTransaction(tx, bc, coinbase, gp) 537 switch err { 538 case core.ErrGasLimitReached: 539 // Pop the current out-of-gas transaction without shifting in the next from the account 540 log.Trace("Gas limit exceeded for current block", "sender", from) 541 txs.Pop() 542 543 case core.ErrNonceTooLow: 544 // New head notification data race between the transaction pool and miner, shift 545 log.Trace("Skipping transaction with low nonce", "sender", from, "nonce", tx.Nonce()) 546 txs.Shift() 547 548 case core.ErrNonceTooHigh: 549 // Reorg notification data race between the transaction pool and miner, skip account = 550 log.Trace("Skipping account with hight nonce", "sender", from, "nonce", tx.Nonce()) 551 txs.Pop() 552 553 case nil: 554 // Everything ok, collect the logs and shift in the next transaction from the same account 555 coalescedLogs = append(coalescedLogs, logs...) 556 env.tcount++ 557 txs.Shift() 558 559 default: 560 // Strange error, discard the transaction and get the next in line (note, the 561 // nonce-too-high clause will prevent us from executing in vain). 562 log.Debug("Transaction failed, account skipped", "hash", tx.Hash(), "err", err) 563 txs.Shift() 564 } 565 } 566 567 if len(coalescedLogs) > 0 || env.tcount > 0 { 568 // make a copy, the state caches the logs and these logs get "upgraded" from pending to mined 569 // logs by filling in the block hash when the block was mined by the local miner. This can 570 // cause a race condition if a log was "upgraded" before the PendingLogsEvent is processed. 571 cpy := make([]*types.Log, len(coalescedLogs)) 572 for i, l := range coalescedLogs { 573 cpy[i] = new(types.Log) 574 *cpy[i] = *l 575 } 576 go func(logs []*types.Log, tcount int) { 577 if len(logs) > 0 { 578 mux.Post(core.PendingLogsEvent{Logs: logs}) 579 } 580 if tcount > 0 { 581 mux.Post(core.PendingStateEvent{}) 582 } 583 }(cpy, env.tcount) 584 } 585 } 586 587 func (env *Work) commitTransaction(tx *types.Transaction, bc *core.BlockChain, coinbase common.Address, gp *core.GasPool) (error, []*types.Log) { 588 snap := env.state.Snapshot() 589 590 receipt, _, err := core.ApplyTransaction(env.config, bc, &coinbase, gp, env.state, env.header, tx, &env.header.GasUsed, vm.Config{}) 591 if err != nil { 592 env.state.RevertToSnapshot(snap) 593 return err, nil 594 } 595 env.txs = append(env.txs, tx) 596 env.receipts = append(env.receipts, receipt) 597 598 return nil, receipt.Logs 599 }