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