github.com/calmw/ethereum@v0.1.1/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 "errors" 21 "fmt" 22 "math/big" 23 "sync" 24 "sync/atomic" 25 "time" 26 27 "github.com/calmw/ethereum/common" 28 "github.com/calmw/ethereum/consensus" 29 "github.com/calmw/ethereum/consensus/misc" 30 "github.com/calmw/ethereum/core" 31 "github.com/calmw/ethereum/core/state" 32 "github.com/calmw/ethereum/core/types" 33 "github.com/calmw/ethereum/event" 34 "github.com/calmw/ethereum/log" 35 "github.com/calmw/ethereum/params" 36 "github.com/calmw/ethereum/trie" 37 mapset "github.com/deckarep/golang-set/v2" 38 ) 39 40 const ( 41 // resultQueueSize is the size of channel listening to sealing result. 42 resultQueueSize = 10 43 44 // txChanSize is the size of channel listening to NewTxsEvent. 45 // The number is referenced from the size of tx pool. 46 txChanSize = 4096 47 48 // chainHeadChanSize is the size of channel listening to ChainHeadEvent. 49 chainHeadChanSize = 10 50 51 // chainSideChanSize is the size of channel listening to ChainSideEvent. 52 chainSideChanSize = 10 53 54 // resubmitAdjustChanSize is the size of resubmitting interval adjustment channel. 55 resubmitAdjustChanSize = 10 56 57 // sealingLogAtDepth is the number of confirmations before logging successful sealing. 58 sealingLogAtDepth = 7 59 60 // minRecommitInterval is the minimal time interval to recreate the sealing block with 61 // any newly arrived transactions. 62 minRecommitInterval = 1 * time.Second 63 64 // maxRecommitInterval is the maximum time interval to recreate the sealing block with 65 // any newly arrived transactions. 66 maxRecommitInterval = 15 * time.Second 67 68 // intervalAdjustRatio is the impact a single interval adjustment has on sealing work 69 // resubmitting interval. 70 intervalAdjustRatio = 0.1 71 72 // intervalAdjustBias is applied during the new resubmit interval calculation in favor of 73 // increasing upper limit or decreasing lower limit so that the limit can be reachable. 74 intervalAdjustBias = 200 * 1000.0 * 1000.0 75 76 // staleThreshold is the maximum depth of the acceptable stale block. 77 staleThreshold = 7 78 ) 79 80 var ( 81 errBlockInterruptedByNewHead = errors.New("new head arrived while building block") 82 errBlockInterruptedByRecommit = errors.New("recommit interrupt while building block") 83 errBlockInterruptedByTimeout = errors.New("timeout while building block") 84 ) 85 86 // environment is the worker's current environment and holds all 87 // information of the sealing block generation. 88 type environment struct { 89 signer types.Signer 90 91 state *state.StateDB // apply state changes here 92 ancestors mapset.Set[common.Hash] // ancestor set (used for checking uncle parent validity) 93 family mapset.Set[common.Hash] // family set (used for checking uncle invalidity) 94 tcount int // tx count in cycle 95 gasPool *core.GasPool // available gas used to pack transactions 96 coinbase common.Address 97 98 header *types.Header 99 txs []*types.Transaction 100 receipts []*types.Receipt 101 uncles map[common.Hash]*types.Header 102 } 103 104 // copy creates a deep copy of environment. 105 func (env *environment) copy() *environment { 106 cpy := &environment{ 107 signer: env.signer, 108 state: env.state.Copy(), 109 ancestors: env.ancestors.Clone(), 110 family: env.family.Clone(), 111 tcount: env.tcount, 112 coinbase: env.coinbase, 113 header: types.CopyHeader(env.header), 114 receipts: copyReceipts(env.receipts), 115 } 116 if env.gasPool != nil { 117 gasPool := *env.gasPool 118 cpy.gasPool = &gasPool 119 } 120 // The content of txs and uncles are immutable, unnecessary 121 // to do the expensive deep copy for them. 122 cpy.txs = make([]*types.Transaction, len(env.txs)) 123 copy(cpy.txs, env.txs) 124 cpy.uncles = make(map[common.Hash]*types.Header) 125 for hash, uncle := range env.uncles { 126 cpy.uncles[hash] = uncle 127 } 128 return cpy 129 } 130 131 // unclelist returns the contained uncles as the list format. 132 func (env *environment) unclelist() []*types.Header { 133 var uncles []*types.Header 134 for _, uncle := range env.uncles { 135 uncles = append(uncles, uncle) 136 } 137 return uncles 138 } 139 140 // discard terminates the background prefetcher go-routine. It should 141 // always be called for all created environment instances otherwise 142 // the go-routine leak can happen. 143 func (env *environment) discard() { 144 if env.state == nil { 145 return 146 } 147 env.state.StopPrefetcher() 148 } 149 150 // task contains all information for consensus engine sealing and result submitting. 151 type task struct { 152 receipts []*types.Receipt 153 state *state.StateDB 154 block *types.Block 155 createdAt time.Time 156 } 157 158 const ( 159 commitInterruptNone int32 = iota 160 commitInterruptNewHead 161 commitInterruptResubmit 162 commitInterruptTimeout 163 ) 164 165 // newWorkReq represents a request for new sealing work submitting with relative interrupt notifier. 166 type newWorkReq struct { 167 interrupt *atomic.Int32 168 noempty bool 169 timestamp int64 170 } 171 172 // newPayloadResult represents a result struct corresponds to payload generation. 173 type newPayloadResult struct { 174 err error 175 block *types.Block 176 fees *big.Int 177 } 178 179 // getWorkReq represents a request for getting a new sealing work with provided parameters. 180 type getWorkReq struct { 181 params *generateParams 182 result chan *newPayloadResult // non-blocking channel 183 } 184 185 // intervalAdjust represents a resubmitting interval adjustment. 186 type intervalAdjust struct { 187 ratio float64 188 inc bool 189 } 190 191 // worker is the main object which takes care of submitting new work to consensus engine 192 // and gathering the sealing result. 193 type worker struct { 194 config *Config 195 chainConfig *params.ChainConfig 196 engine consensus.Engine 197 eth Backend 198 chain *core.BlockChain 199 200 // Feeds 201 pendingLogsFeed event.Feed 202 203 // Subscriptions 204 mux *event.TypeMux 205 txsCh chan core.NewTxsEvent 206 txsSub event.Subscription 207 chainHeadCh chan core.ChainHeadEvent 208 chainHeadSub event.Subscription 209 chainSideCh chan core.ChainSideEvent 210 chainSideSub event.Subscription 211 212 // Channels 213 newWorkCh chan *newWorkReq 214 getWorkCh chan *getWorkReq 215 taskCh chan *task 216 resultCh chan *types.Block 217 startCh chan struct{} 218 exitCh chan struct{} 219 resubmitIntervalCh chan time.Duration 220 resubmitAdjustCh chan *intervalAdjust 221 222 wg sync.WaitGroup 223 224 current *environment // An environment for current running cycle. 225 localUncles map[common.Hash]*types.Block // A set of side blocks generated locally as the possible uncle blocks. 226 remoteUncles map[common.Hash]*types.Block // A set of side blocks as the possible uncle blocks. 227 unconfirmed *unconfirmedBlocks // A set of locally mined blocks pending canonicalness confirmations. 228 229 mu sync.RWMutex // The lock used to protect the coinbase and extra fields 230 coinbase common.Address 231 extra []byte 232 233 pendingMu sync.RWMutex 234 pendingTasks map[common.Hash]*task 235 236 snapshotMu sync.RWMutex // The lock used to protect the snapshots below 237 snapshotBlock *types.Block 238 snapshotReceipts types.Receipts 239 snapshotState *state.StateDB 240 241 // atomic status counters 242 running atomic.Bool // The indicator whether the consensus engine is running or not. 243 newTxs atomic.Int32 // New arrival transaction count since last sealing work submitting. 244 245 // noempty is the flag used to control whether the feature of pre-seal empty 246 // block is enabled. The default value is false(pre-seal is enabled by default). 247 // But in some special scenario the consensus engine will seal blocks instantaneously, 248 // in this case this feature will add all empty blocks into canonical chain 249 // non-stop and no real transaction will be included. 250 noempty atomic.Bool 251 252 // newpayloadTimeout is the maximum timeout allowance for creating payload. 253 // The default value is 2 seconds but node operator can set it to arbitrary 254 // large value. A large timeout allowance may cause Geth to fail creating 255 // a non-empty payload within the specified time and eventually miss the slot 256 // in case there are some computation expensive transactions in txpool. 257 newpayloadTimeout time.Duration 258 259 // recommit is the time interval to re-create sealing work or to re-build 260 // payload in proof-of-stake stage. 261 recommit time.Duration 262 263 // External functions 264 isLocalBlock func(header *types.Header) bool // Function used to determine whether the specified block is mined by local miner. 265 266 // Test hooks 267 newTaskHook func(*task) // Method to call upon receiving a new sealing task. 268 skipSealHook func(*task) bool // Method to decide whether skipping the sealing. 269 fullTaskHook func() // Method to call before pushing the full sealing task. 270 resubmitHook func(time.Duration, time.Duration) // Method to call upon updating resubmitting interval. 271 } 272 273 func newWorker(config *Config, chainConfig *params.ChainConfig, engine consensus.Engine, eth Backend, mux *event.TypeMux, isLocalBlock func(header *types.Header) bool, init bool) *worker { 274 worker := &worker{ 275 config: config, 276 chainConfig: chainConfig, 277 engine: engine, 278 eth: eth, 279 chain: eth.BlockChain(), 280 mux: mux, 281 isLocalBlock: isLocalBlock, 282 localUncles: make(map[common.Hash]*types.Block), 283 remoteUncles: make(map[common.Hash]*types.Block), 284 unconfirmed: newUnconfirmedBlocks(eth.BlockChain(), sealingLogAtDepth), 285 coinbase: config.Etherbase, 286 extra: config.ExtraData, 287 pendingTasks: make(map[common.Hash]*task), 288 txsCh: make(chan core.NewTxsEvent, txChanSize), 289 chainHeadCh: make(chan core.ChainHeadEvent, chainHeadChanSize), 290 chainSideCh: make(chan core.ChainSideEvent, chainSideChanSize), 291 newWorkCh: make(chan *newWorkReq), 292 getWorkCh: make(chan *getWorkReq), 293 taskCh: make(chan *task), 294 resultCh: make(chan *types.Block, resultQueueSize), 295 startCh: make(chan struct{}, 1), 296 exitCh: make(chan struct{}), 297 resubmitIntervalCh: make(chan time.Duration), 298 resubmitAdjustCh: make(chan *intervalAdjust, resubmitAdjustChanSize), 299 } 300 // Subscribe NewTxsEvent for tx pool 301 worker.txsSub = eth.TxPool().SubscribeNewTxsEvent(worker.txsCh) 302 // Subscribe events for blockchain 303 worker.chainHeadSub = eth.BlockChain().SubscribeChainHeadEvent(worker.chainHeadCh) 304 worker.chainSideSub = eth.BlockChain().SubscribeChainSideEvent(worker.chainSideCh) 305 306 // Sanitize recommit interval if the user-specified one is too short. 307 recommit := worker.config.Recommit 308 if recommit < minRecommitInterval { 309 log.Warn("Sanitizing miner recommit interval", "provided", recommit, "updated", minRecommitInterval) 310 recommit = minRecommitInterval 311 } 312 worker.recommit = recommit 313 314 // Sanitize the timeout config for creating payload. 315 newpayloadTimeout := worker.config.NewPayloadTimeout 316 if newpayloadTimeout == 0 { 317 log.Warn("Sanitizing new payload timeout to default", "provided", newpayloadTimeout, "updated", DefaultConfig.NewPayloadTimeout) 318 newpayloadTimeout = DefaultConfig.NewPayloadTimeout 319 } 320 if newpayloadTimeout < time.Millisecond*100 { 321 log.Warn("Low payload timeout may cause high amount of non-full blocks", "provided", newpayloadTimeout, "default", DefaultConfig.NewPayloadTimeout) 322 } 323 worker.newpayloadTimeout = newpayloadTimeout 324 325 worker.wg.Add(4) 326 go worker.mainLoop() 327 go worker.newWorkLoop(recommit) 328 go worker.resultLoop() 329 go worker.taskLoop() 330 331 // Submit first work to initialize pending state. 332 if init { 333 worker.startCh <- struct{}{} 334 } 335 return worker 336 } 337 338 // setEtherbase sets the etherbase used to initialize the block coinbase field. 339 func (w *worker) setEtherbase(addr common.Address) { 340 w.mu.Lock() 341 defer w.mu.Unlock() 342 w.coinbase = addr 343 } 344 345 // etherbase retrieves the configured etherbase address. 346 func (w *worker) etherbase() common.Address { 347 w.mu.RLock() 348 defer w.mu.RUnlock() 349 return w.coinbase 350 } 351 352 func (w *worker) setGasCeil(ceil uint64) { 353 w.mu.Lock() 354 defer w.mu.Unlock() 355 w.config.GasCeil = ceil 356 } 357 358 // setExtra sets the content used to initialize the block extra field. 359 func (w *worker) setExtra(extra []byte) { 360 w.mu.Lock() 361 defer w.mu.Unlock() 362 w.extra = extra 363 } 364 365 // setRecommitInterval updates the interval for miner sealing work recommitting. 366 func (w *worker) setRecommitInterval(interval time.Duration) { 367 select { 368 case w.resubmitIntervalCh <- interval: 369 case <-w.exitCh: 370 } 371 } 372 373 // disablePreseal disables pre-sealing feature 374 func (w *worker) disablePreseal() { 375 w.noempty.Store(true) 376 } 377 378 // enablePreseal enables pre-sealing feature 379 func (w *worker) enablePreseal() { 380 w.noempty.Store(false) 381 } 382 383 // pending returns the pending state and corresponding block. 384 func (w *worker) pending() (*types.Block, *state.StateDB) { 385 // return a snapshot to avoid contention on currentMu mutex 386 w.snapshotMu.RLock() 387 defer w.snapshotMu.RUnlock() 388 if w.snapshotState == nil { 389 return nil, nil 390 } 391 return w.snapshotBlock, w.snapshotState.Copy() 392 } 393 394 // pendingBlock returns pending block. 395 func (w *worker) pendingBlock() *types.Block { 396 // return a snapshot to avoid contention on currentMu mutex 397 w.snapshotMu.RLock() 398 defer w.snapshotMu.RUnlock() 399 return w.snapshotBlock 400 } 401 402 // pendingBlockAndReceipts returns pending block and corresponding receipts. 403 func (w *worker) pendingBlockAndReceipts() (*types.Block, types.Receipts) { 404 // return a snapshot to avoid contention on currentMu mutex 405 w.snapshotMu.RLock() 406 defer w.snapshotMu.RUnlock() 407 return w.snapshotBlock, w.snapshotReceipts 408 } 409 410 // start sets the running status as 1 and triggers new work submitting. 411 func (w *worker) start() { 412 w.running.Store(true) 413 w.startCh <- struct{}{} 414 } 415 416 // stop sets the running status as 0. 417 func (w *worker) stop() { 418 w.running.Store(false) 419 } 420 421 // isRunning returns an indicator whether worker is running or not. 422 func (w *worker) isRunning() bool { 423 return w.running.Load() 424 } 425 426 // close terminates all background threads maintained by the worker. 427 // Note the worker does not support being closed multiple times. 428 func (w *worker) close() { 429 w.running.Store(false) 430 close(w.exitCh) 431 w.wg.Wait() 432 } 433 434 // recalcRecommit recalculates the resubmitting interval upon feedback. 435 func recalcRecommit(minRecommit, prev time.Duration, target float64, inc bool) time.Duration { 436 var ( 437 prevF = float64(prev.Nanoseconds()) 438 next float64 439 ) 440 if inc { 441 next = prevF*(1-intervalAdjustRatio) + intervalAdjustRatio*(target+intervalAdjustBias) 442 max := float64(maxRecommitInterval.Nanoseconds()) 443 if next > max { 444 next = max 445 } 446 } else { 447 next = prevF*(1-intervalAdjustRatio) + intervalAdjustRatio*(target-intervalAdjustBias) 448 min := float64(minRecommit.Nanoseconds()) 449 if next < min { 450 next = min 451 } 452 } 453 return time.Duration(int64(next)) 454 } 455 456 // newWorkLoop is a standalone goroutine to submit new sealing work upon received events. 457 func (w *worker) newWorkLoop(recommit time.Duration) { 458 defer w.wg.Done() 459 var ( 460 interrupt *atomic.Int32 461 minRecommit = recommit // minimal resubmit interval specified by user. 462 timestamp int64 // timestamp for each round of sealing. 463 ) 464 465 timer := time.NewTimer(0) 466 defer timer.Stop() 467 <-timer.C // discard the initial tick 468 469 // commit aborts in-flight transaction execution with given signal and resubmits a new one. 470 commit := func(noempty bool, s int32) { 471 if interrupt != nil { 472 interrupt.Store(s) 473 } 474 interrupt = new(atomic.Int32) 475 select { 476 case w.newWorkCh <- &newWorkReq{interrupt: interrupt, noempty: noempty, timestamp: timestamp}: 477 case <-w.exitCh: 478 return 479 } 480 timer.Reset(recommit) 481 w.newTxs.Store(0) 482 } 483 // clearPending cleans the stale pending tasks. 484 clearPending := func(number uint64) { 485 w.pendingMu.Lock() 486 for h, t := range w.pendingTasks { 487 if t.block.NumberU64()+staleThreshold <= number { 488 delete(w.pendingTasks, h) 489 } 490 } 491 w.pendingMu.Unlock() 492 } 493 494 for { 495 select { 496 case <-w.startCh: 497 clearPending(w.chain.CurrentBlock().Number.Uint64()) 498 timestamp = time.Now().Unix() 499 commit(false, commitInterruptNewHead) 500 501 case head := <-w.chainHeadCh: 502 clearPending(head.Block.NumberU64()) 503 timestamp = time.Now().Unix() 504 commit(false, commitInterruptNewHead) 505 506 case <-timer.C: 507 // If sealing is running resubmit a new work cycle periodically to pull in 508 // higher priced transactions. Disable this overhead for pending blocks. 509 if w.isRunning() && (w.chainConfig.Clique == nil || w.chainConfig.Clique.Period > 0) { 510 // Short circuit if no new transaction arrives. 511 if w.newTxs.Load() == 0 { 512 timer.Reset(recommit) 513 continue 514 } 515 commit(true, commitInterruptResubmit) 516 } 517 518 case interval := <-w.resubmitIntervalCh: 519 // Adjust resubmit interval explicitly by user. 520 if interval < minRecommitInterval { 521 log.Warn("Sanitizing miner recommit interval", "provided", interval, "updated", minRecommitInterval) 522 interval = minRecommitInterval 523 } 524 log.Info("Miner recommit interval update", "from", minRecommit, "to", interval) 525 minRecommit, recommit = interval, interval 526 527 if w.resubmitHook != nil { 528 w.resubmitHook(minRecommit, recommit) 529 } 530 531 case adjust := <-w.resubmitAdjustCh: 532 // Adjust resubmit interval by feedback. 533 if adjust.inc { 534 before := recommit 535 target := float64(recommit.Nanoseconds()) / adjust.ratio 536 recommit = recalcRecommit(minRecommit, recommit, target, true) 537 log.Trace("Increase miner recommit interval", "from", before, "to", recommit) 538 } else { 539 before := recommit 540 recommit = recalcRecommit(minRecommit, recommit, float64(minRecommit.Nanoseconds()), false) 541 log.Trace("Decrease miner recommit interval", "from", before, "to", recommit) 542 } 543 544 if w.resubmitHook != nil { 545 w.resubmitHook(minRecommit, recommit) 546 } 547 548 case <-w.exitCh: 549 return 550 } 551 } 552 } 553 554 // mainLoop is responsible for generating and submitting sealing work based on 555 // the received event. It can support two modes: automatically generate task and 556 // submit it or return task according to given parameters for various proposes. 557 func (w *worker) mainLoop() { 558 defer w.wg.Done() 559 defer w.txsSub.Unsubscribe() 560 defer w.chainHeadSub.Unsubscribe() 561 defer w.chainSideSub.Unsubscribe() 562 defer func() { 563 if w.current != nil { 564 w.current.discard() 565 } 566 }() 567 568 cleanTicker := time.NewTicker(time.Second * 10) 569 defer cleanTicker.Stop() 570 571 for { 572 select { 573 case req := <-w.newWorkCh: 574 w.commitWork(req.interrupt, req.noempty, req.timestamp) 575 576 case req := <-w.getWorkCh: 577 block, fees, err := w.generateWork(req.params) 578 req.result <- &newPayloadResult{ 579 err: err, 580 block: block, 581 fees: fees, 582 } 583 case ev := <-w.chainSideCh: 584 // Short circuit for duplicate side blocks 585 if _, exist := w.localUncles[ev.Block.Hash()]; exist { 586 continue 587 } 588 if _, exist := w.remoteUncles[ev.Block.Hash()]; exist { 589 continue 590 } 591 // Add side block to possible uncle block set depending on the author. 592 if w.isLocalBlock != nil && w.isLocalBlock(ev.Block.Header()) { 593 w.localUncles[ev.Block.Hash()] = ev.Block 594 } else { 595 w.remoteUncles[ev.Block.Hash()] = ev.Block 596 } 597 // If our sealing block contains less than 2 uncle blocks, 598 // add the new uncle block if valid and regenerate a new 599 // sealing block for higher profit. 600 if w.isRunning() && w.current != nil && len(w.current.uncles) < 2 { 601 start := time.Now() 602 if err := w.commitUncle(w.current, ev.Block.Header()); err == nil { 603 w.commit(w.current.copy(), nil, true, start) 604 } 605 } 606 607 case <-cleanTicker.C: 608 chainHead := w.chain.CurrentBlock() 609 for hash, uncle := range w.localUncles { 610 if uncle.NumberU64()+staleThreshold <= chainHead.Number.Uint64() { 611 delete(w.localUncles, hash) 612 } 613 } 614 for hash, uncle := range w.remoteUncles { 615 if uncle.NumberU64()+staleThreshold <= chainHead.Number.Uint64() { 616 delete(w.remoteUncles, hash) 617 } 618 } 619 620 case ev := <-w.txsCh: 621 // Apply transactions to the pending state if we're not sealing 622 // 623 // Note all transactions received may not be continuous with transactions 624 // already included in the current sealing block. These transactions will 625 // be automatically eliminated. 626 if !w.isRunning() && w.current != nil { 627 // If block is already full, abort 628 if gp := w.current.gasPool; gp != nil && gp.Gas() < params.TxGas { 629 continue 630 } 631 txs := make(map[common.Address]types.Transactions, len(ev.Txs)) 632 for _, tx := range ev.Txs { 633 acc, _ := types.Sender(w.current.signer, tx) 634 txs[acc] = append(txs[acc], tx) 635 } 636 txset := types.NewTransactionsByPriceAndNonce(w.current.signer, txs, w.current.header.BaseFee) 637 tcount := w.current.tcount 638 w.commitTransactions(w.current, txset, nil) 639 640 // Only update the snapshot if any new transactions were added 641 // to the pending block 642 if tcount != w.current.tcount { 643 w.updateSnapshot(w.current) 644 } 645 } else { 646 // Special case, if the consensus engine is 0 period clique(dev mode), 647 // submit sealing work here since all empty submission will be rejected 648 // by clique. Of course the advance sealing(empty submission) is disabled. 649 if w.chainConfig.Clique != nil && w.chainConfig.Clique.Period == 0 { 650 w.commitWork(nil, true, time.Now().Unix()) 651 } 652 } 653 w.newTxs.Add(int32(len(ev.Txs))) 654 655 // System stopped 656 case <-w.exitCh: 657 return 658 case <-w.txsSub.Err(): 659 return 660 case <-w.chainHeadSub.Err(): 661 return 662 case <-w.chainSideSub.Err(): 663 return 664 } 665 } 666 } 667 668 // taskLoop is a standalone goroutine to fetch sealing task from the generator and 669 // push them to consensus engine. 670 func (w *worker) taskLoop() { 671 defer w.wg.Done() 672 var ( 673 stopCh chan struct{} 674 prev common.Hash 675 ) 676 677 // interrupt aborts the in-flight sealing task. 678 interrupt := func() { 679 if stopCh != nil { 680 close(stopCh) 681 stopCh = nil 682 } 683 } 684 for { 685 select { 686 case task := <-w.taskCh: 687 if w.newTaskHook != nil { 688 w.newTaskHook(task) 689 } 690 // Reject duplicate sealing work due to resubmitting. 691 sealHash := w.engine.SealHash(task.block.Header()) 692 if sealHash == prev { 693 continue 694 } 695 // Interrupt previous sealing operation 696 interrupt() 697 stopCh, prev = make(chan struct{}), sealHash 698 699 if w.skipSealHook != nil && w.skipSealHook(task) { 700 continue 701 } 702 w.pendingMu.Lock() 703 w.pendingTasks[sealHash] = task 704 w.pendingMu.Unlock() 705 706 if err := w.engine.Seal(w.chain, task.block, w.resultCh, stopCh); err != nil { 707 log.Warn("Block sealing failed", "err", err) 708 w.pendingMu.Lock() 709 delete(w.pendingTasks, sealHash) 710 w.pendingMu.Unlock() 711 } 712 case <-w.exitCh: 713 interrupt() 714 return 715 } 716 } 717 } 718 719 // resultLoop is a standalone goroutine to handle sealing result submitting 720 // and flush relative data to the database. 721 func (w *worker) resultLoop() { 722 defer w.wg.Done() 723 for { 724 select { 725 case block := <-w.resultCh: 726 // Short circuit when receiving empty result. 727 if block == nil { 728 continue 729 } 730 // Short circuit when receiving duplicate result caused by resubmitting. 731 if w.chain.HasBlock(block.Hash(), block.NumberU64()) { 732 continue 733 } 734 var ( 735 sealhash = w.engine.SealHash(block.Header()) 736 hash = block.Hash() 737 ) 738 w.pendingMu.RLock() 739 task, exist := w.pendingTasks[sealhash] 740 w.pendingMu.RUnlock() 741 if !exist { 742 log.Error("Block found but no relative pending task", "number", block.Number(), "sealhash", sealhash, "hash", hash) 743 continue 744 } 745 // Different block could share same sealhash, deep copy here to prevent write-write conflict. 746 var ( 747 receipts = make([]*types.Receipt, len(task.receipts)) 748 logs []*types.Log 749 ) 750 for i, taskReceipt := range task.receipts { 751 receipt := new(types.Receipt) 752 receipts[i] = receipt 753 *receipt = *taskReceipt 754 755 // add block location fields 756 receipt.BlockHash = hash 757 receipt.BlockNumber = block.Number() 758 receipt.TransactionIndex = uint(i) 759 760 // Update the block hash in all logs since it is now available and not when the 761 // receipt/log of individual transactions were created. 762 receipt.Logs = make([]*types.Log, len(taskReceipt.Logs)) 763 for i, taskLog := range taskReceipt.Logs { 764 log := new(types.Log) 765 receipt.Logs[i] = log 766 *log = *taskLog 767 log.BlockHash = hash 768 } 769 logs = append(logs, receipt.Logs...) 770 } 771 // Commit block and state to database. 772 _, err := w.chain.WriteBlockAndSetHead(block, receipts, logs, task.state, true) 773 if err != nil { 774 log.Error("Failed writing block to chain", "err", err) 775 continue 776 } 777 log.Info("Successfully sealed new block", "number", block.Number(), "sealhash", sealhash, "hash", hash, 778 "elapsed", common.PrettyDuration(time.Since(task.createdAt))) 779 780 // Broadcast the block and announce chain insertion event 781 w.mux.Post(core.NewMinedBlockEvent{Block: block}) 782 783 // Insert the block into the set of pending ones to resultLoop for confirmations 784 w.unconfirmed.Insert(block.NumberU64(), block.Hash()) 785 786 case <-w.exitCh: 787 return 788 } 789 } 790 } 791 792 // makeEnv creates a new environment for the sealing block. 793 func (w *worker) makeEnv(parent *types.Header, header *types.Header, coinbase common.Address) (*environment, error) { 794 // Retrieve the parent state to execute on top and start a prefetcher for 795 // the miner to speed block sealing up a bit. 796 state, err := w.chain.StateAt(parent.Root) 797 if err != nil { 798 return nil, err 799 } 800 state.StartPrefetcher("miner") 801 802 // Note the passed coinbase may be different with header.Coinbase. 803 env := &environment{ 804 signer: types.MakeSigner(w.chainConfig, header.Number, header.Time), 805 state: state, 806 coinbase: coinbase, 807 ancestors: mapset.NewSet[common.Hash](), 808 family: mapset.NewSet[common.Hash](), 809 header: header, 810 uncles: make(map[common.Hash]*types.Header), 811 } 812 // when 08 is processed ancestors contain 07 (quick block) 813 for _, ancestor := range w.chain.GetBlocksFromHash(parent.Hash(), 7) { 814 for _, uncle := range ancestor.Uncles() { 815 env.family.Add(uncle.Hash()) 816 } 817 env.family.Add(ancestor.Hash()) 818 env.ancestors.Add(ancestor.Hash()) 819 } 820 // Keep track of transactions which return errors so they can be removed 821 env.tcount = 0 822 return env, nil 823 } 824 825 // commitUncle adds the given block to uncle block set, returns error if failed to add. 826 func (w *worker) commitUncle(env *environment, uncle *types.Header) error { 827 if w.isTTDReached(env.header) { 828 return errors.New("ignore uncle for beacon block") 829 } 830 hash := uncle.Hash() 831 if _, exist := env.uncles[hash]; exist { 832 return errors.New("uncle not unique") 833 } 834 if env.header.ParentHash == uncle.ParentHash { 835 return errors.New("uncle is sibling") 836 } 837 if !env.ancestors.Contains(uncle.ParentHash) { 838 return errors.New("uncle's parent unknown") 839 } 840 if env.family.Contains(hash) { 841 return errors.New("uncle already included") 842 } 843 env.uncles[hash] = uncle 844 return nil 845 } 846 847 // updateSnapshot updates pending snapshot block, receipts and state. 848 func (w *worker) updateSnapshot(env *environment) { 849 w.snapshotMu.Lock() 850 defer w.snapshotMu.Unlock() 851 852 w.snapshotBlock = types.NewBlock( 853 env.header, 854 env.txs, 855 env.unclelist(), 856 env.receipts, 857 trie.NewStackTrie(nil), 858 ) 859 w.snapshotReceipts = copyReceipts(env.receipts) 860 w.snapshotState = env.state.Copy() 861 } 862 863 func (w *worker) commitTransaction(env *environment, tx *types.Transaction) ([]*types.Log, error) { 864 var ( 865 snap = env.state.Snapshot() 866 gp = env.gasPool.Gas() 867 ) 868 receipt, err := core.ApplyTransaction(w.chainConfig, w.chain, &env.coinbase, env.gasPool, env.state, env.header, tx, &env.header.GasUsed, *w.chain.GetVMConfig()) 869 if err != nil { 870 env.state.RevertToSnapshot(snap) 871 env.gasPool.SetGas(gp) 872 return nil, err 873 } 874 env.txs = append(env.txs, tx) 875 env.receipts = append(env.receipts, receipt) 876 877 return receipt.Logs, nil 878 } 879 880 func (w *worker) commitTransactions(env *environment, txs *types.TransactionsByPriceAndNonce, interrupt *atomic.Int32) error { 881 gasLimit := env.header.GasLimit 882 if env.gasPool == nil { 883 env.gasPool = new(core.GasPool).AddGas(gasLimit) 884 } 885 var coalescedLogs []*types.Log 886 887 for { 888 // Check interruption signal and abort building if it's fired. 889 if interrupt != nil { 890 if signal := interrupt.Load(); signal != commitInterruptNone { 891 return signalToErr(signal) 892 } 893 } 894 // If we don't have enough gas for any further transactions then we're done. 895 if env.gasPool.Gas() < params.TxGas { 896 log.Trace("Not enough gas for further transactions", "have", env.gasPool, "want", params.TxGas) 897 break 898 } 899 // Retrieve the next transaction and abort if all done. 900 tx := txs.Peek() 901 if tx == nil { 902 break 903 } 904 // Error may be ignored here. The error has already been checked 905 // during transaction acceptance is the transaction pool. 906 from, _ := types.Sender(env.signer, tx) 907 908 // Check whether the tx is replay protected. If we're not in the EIP155 hf 909 // phase, start ignoring the sender until we do. 910 if tx.Protected() && !w.chainConfig.IsEIP155(env.header.Number) { 911 log.Trace("Ignoring reply protected transaction", "hash", tx.Hash(), "eip155", w.chainConfig.EIP155Block) 912 913 txs.Pop() 914 continue 915 } 916 // Start executing the transaction 917 env.state.SetTxContext(tx.Hash(), env.tcount) 918 919 logs, err := w.commitTransaction(env, tx) 920 switch { 921 case errors.Is(err, core.ErrNonceTooLow): 922 // New head notification data race between the transaction pool and miner, shift 923 log.Trace("Skipping transaction with low nonce", "sender", from, "nonce", tx.Nonce()) 924 txs.Shift() 925 926 case errors.Is(err, nil): 927 // Everything ok, collect the logs and shift in the next transaction from the same account 928 coalescedLogs = append(coalescedLogs, logs...) 929 env.tcount++ 930 txs.Shift() 931 932 default: 933 // Transaction is regarded as invalid, drop all consecutive transactions from 934 // the same sender because of `nonce-too-high` clause. 935 log.Debug("Transaction failed, account skipped", "hash", tx.Hash(), "err", err) 936 txs.Pop() 937 } 938 } 939 if !w.isRunning() && len(coalescedLogs) > 0 { 940 // We don't push the pendingLogsEvent while we are sealing. The reason is that 941 // when we are sealing, the worker will regenerate a sealing block every 3 seconds. 942 // In order to avoid pushing the repeated pendingLog, we disable the pending log pushing. 943 944 // make a copy, the state caches the logs and these logs get "upgraded" from pending to mined 945 // logs by filling in the block hash when the block was mined by the local miner. This can 946 // cause a race condition if a log was "upgraded" before the PendingLogsEvent is processed. 947 cpy := make([]*types.Log, len(coalescedLogs)) 948 for i, l := range coalescedLogs { 949 cpy[i] = new(types.Log) 950 *cpy[i] = *l 951 } 952 w.pendingLogsFeed.Send(cpy) 953 } 954 return nil 955 } 956 957 // generateParams wraps various of settings for generating sealing task. 958 type generateParams struct { 959 timestamp uint64 // The timstamp for sealing task 960 forceTime bool // Flag whether the given timestamp is immutable or not 961 parentHash common.Hash // Parent block hash, empty means the latest chain head 962 coinbase common.Address // The fee recipient address for including transaction 963 random common.Hash // The randomness generated by beacon chain, empty before the merge 964 withdrawals types.Withdrawals // List of withdrawals to include in block. 965 noUncle bool // Flag whether the uncle block inclusion is allowed 966 noTxs bool // Flag whether an empty block without any transaction is expected 967 } 968 969 // prepareWork constructs the sealing task according to the given parameters, 970 // either based on the last chain head or specified parent. In this function 971 // the pending transactions are not filled yet, only the empty task returned. 972 func (w *worker) prepareWork(genParams *generateParams) (*environment, error) { 973 w.mu.RLock() 974 defer w.mu.RUnlock() 975 976 // Find the parent block for sealing task 977 parent := w.chain.CurrentBlock() 978 if genParams.parentHash != (common.Hash{}) { 979 block := w.chain.GetBlockByHash(genParams.parentHash) 980 if block == nil { 981 return nil, fmt.Errorf("missing parent") 982 } 983 parent = block.Header() 984 } 985 // Sanity check the timestamp correctness, recap the timestamp 986 // to parent+1 if the mutation is allowed. 987 timestamp := genParams.timestamp 988 if parent.Time >= timestamp { 989 if genParams.forceTime { 990 return nil, fmt.Errorf("invalid timestamp, parent %d given %d", parent.Time, timestamp) 991 } 992 timestamp = parent.Time + 1 993 } 994 // Construct the sealing block header. 995 header := &types.Header{ 996 ParentHash: parent.Hash(), 997 Number: new(big.Int).Add(parent.Number, common.Big1), 998 GasLimit: core.CalcGasLimit(parent.GasLimit, w.config.GasCeil), 999 Time: timestamp, 1000 Coinbase: genParams.coinbase, 1001 } 1002 // Set the extra field. 1003 if len(w.extra) != 0 { 1004 header.Extra = w.extra 1005 } 1006 // Set the randomness field from the beacon chain if it's available. 1007 if genParams.random != (common.Hash{}) { 1008 header.MixDigest = genParams.random 1009 } 1010 // Set baseFee and GasLimit if we are on an EIP-1559 chain 1011 if w.chainConfig.IsLondon(header.Number) { 1012 header.BaseFee = misc.CalcBaseFee(w.chainConfig, parent) 1013 if !w.chainConfig.IsLondon(parent.Number) { 1014 parentGasLimit := parent.GasLimit * w.chainConfig.ElasticityMultiplier() 1015 header.GasLimit = core.CalcGasLimit(parentGasLimit, w.config.GasCeil) 1016 } 1017 } 1018 // Run the consensus preparation with the default or customized consensus engine. 1019 if err := w.engine.Prepare(w.chain, header); err != nil { 1020 log.Error("Failed to prepare header for sealing", "err", err) 1021 return nil, err 1022 } 1023 // Could potentially happen if starting to mine in an odd state. 1024 // Note genParams.coinbase can be different with header.Coinbase 1025 // since clique algorithm can modify the coinbase field in header. 1026 env, err := w.makeEnv(parent, header, genParams.coinbase) 1027 if err != nil { 1028 log.Error("Failed to create sealing context", "err", err) 1029 return nil, err 1030 } 1031 // Accumulate the uncles for the sealing work only if it's allowed. 1032 if !genParams.noUncle { 1033 commitUncles := func(blocks map[common.Hash]*types.Block) { 1034 for hash, uncle := range blocks { 1035 if len(env.uncles) == 2 { 1036 break 1037 } 1038 if err := w.commitUncle(env, uncle.Header()); err != nil { 1039 log.Trace("Possible uncle rejected", "hash", hash, "reason", err) 1040 } else { 1041 log.Debug("Committing new uncle to block", "hash", hash) 1042 } 1043 } 1044 } 1045 // Prefer to locally generated uncle 1046 commitUncles(w.localUncles) 1047 commitUncles(w.remoteUncles) 1048 } 1049 return env, nil 1050 } 1051 1052 // fillTransactions retrieves the pending transactions from the txpool and fills them 1053 // into the given sealing block. The transaction selection and ordering strategy can 1054 // be customized with the plugin in the future. 1055 func (w *worker) fillTransactions(interrupt *atomic.Int32, env *environment) error { 1056 // Split the pending transactions into locals and remotes 1057 // Fill the block with all available pending transactions. 1058 pending := w.eth.TxPool().Pending(true) 1059 localTxs, remoteTxs := make(map[common.Address]types.Transactions), pending 1060 for _, account := range w.eth.TxPool().Locals() { 1061 if txs := remoteTxs[account]; len(txs) > 0 { 1062 delete(remoteTxs, account) 1063 localTxs[account] = txs 1064 } 1065 } 1066 if len(localTxs) > 0 { 1067 txs := types.NewTransactionsByPriceAndNonce(env.signer, localTxs, env.header.BaseFee) 1068 if err := w.commitTransactions(env, txs, interrupt); err != nil { 1069 return err 1070 } 1071 } 1072 if len(remoteTxs) > 0 { 1073 txs := types.NewTransactionsByPriceAndNonce(env.signer, remoteTxs, env.header.BaseFee) 1074 if err := w.commitTransactions(env, txs, interrupt); err != nil { 1075 return err 1076 } 1077 } 1078 return nil 1079 } 1080 1081 // generateWork generates a sealing block based on the given parameters. 1082 func (w *worker) generateWork(params *generateParams) (*types.Block, *big.Int, error) { 1083 work, err := w.prepareWork(params) 1084 if err != nil { 1085 return nil, nil, err 1086 } 1087 defer work.discard() 1088 1089 if !params.noTxs { 1090 interrupt := new(atomic.Int32) 1091 timer := time.AfterFunc(w.newpayloadTimeout, func() { 1092 interrupt.Store(commitInterruptTimeout) 1093 }) 1094 defer timer.Stop() 1095 1096 err := w.fillTransactions(interrupt, work) 1097 if errors.Is(err, errBlockInterruptedByTimeout) { 1098 log.Warn("Block building is interrupted", "allowance", common.PrettyDuration(w.newpayloadTimeout)) 1099 } 1100 } 1101 block, err := w.engine.FinalizeAndAssemble(w.chain, work.header, work.state, work.txs, work.unclelist(), work.receipts, params.withdrawals) 1102 if err != nil { 1103 return nil, nil, err 1104 } 1105 return block, totalFees(block, work.receipts), nil 1106 } 1107 1108 // commitWork generates several new sealing tasks based on the parent block 1109 // and submit them to the sealer. 1110 func (w *worker) commitWork(interrupt *atomic.Int32, noempty bool, timestamp int64) { 1111 start := time.Now() 1112 1113 // Set the coinbase if the worker is running or it's required 1114 var coinbase common.Address 1115 if w.isRunning() { 1116 coinbase = w.etherbase() 1117 if coinbase == (common.Address{}) { 1118 log.Error("Refusing to mine without etherbase") 1119 return 1120 } 1121 } 1122 work, err := w.prepareWork(&generateParams{ 1123 timestamp: uint64(timestamp), 1124 coinbase: coinbase, 1125 }) 1126 if err != nil { 1127 return 1128 } 1129 // Create an empty block based on temporary copied state for 1130 // sealing in advance without waiting block execution finished. 1131 if !noempty && !w.noempty.Load() { 1132 w.commit(work.copy(), nil, false, start) 1133 } 1134 // Fill pending transactions from the txpool into the block. 1135 err = w.fillTransactions(interrupt, work) 1136 switch { 1137 case err == nil: 1138 // The entire block is filled, decrease resubmit interval in case 1139 // of current interval is larger than the user-specified one. 1140 w.resubmitAdjustCh <- &intervalAdjust{inc: false} 1141 1142 case errors.Is(err, errBlockInterruptedByRecommit): 1143 // Notify resubmit loop to increase resubmitting interval if the 1144 // interruption is due to frequent commits. 1145 gaslimit := work.header.GasLimit 1146 ratio := float64(gaslimit-work.gasPool.Gas()) / float64(gaslimit) 1147 if ratio < 0.1 { 1148 ratio = 0.1 1149 } 1150 w.resubmitAdjustCh <- &intervalAdjust{ 1151 ratio: ratio, 1152 inc: true, 1153 } 1154 1155 case errors.Is(err, errBlockInterruptedByNewHead): 1156 // If the block building is interrupted by newhead event, discard it 1157 // totally. Committing the interrupted block introduces unnecessary 1158 // delay, and possibly causes miner to mine on the previous head, 1159 // which could result in higher uncle rate. 1160 work.discard() 1161 return 1162 } 1163 // Submit the generated block for consensus sealing. 1164 w.commit(work.copy(), w.fullTaskHook, true, start) 1165 1166 // Swap out the old work with the new one, terminating any leftover 1167 // prefetcher processes in the mean time and starting a new one. 1168 if w.current != nil { 1169 w.current.discard() 1170 } 1171 w.current = work 1172 } 1173 1174 // commit runs any post-transaction state modifications, assembles the final block 1175 // and commits new work if consensus engine is running. 1176 // Note the assumption is held that the mutation is allowed to the passed env, do 1177 // the deep copy first. 1178 func (w *worker) commit(env *environment, interval func(), update bool, start time.Time) error { 1179 if w.isRunning() { 1180 if interval != nil { 1181 interval() 1182 } 1183 // Create a local environment copy, avoid the data race with snapshot state. 1184 // https://github.com/ethereum/go-ethereum/issues/24299 1185 env := env.copy() 1186 // Withdrawals are set to nil here, because this is only called in PoW. 1187 block, err := w.engine.FinalizeAndAssemble(w.chain, env.header, env.state, env.txs, env.unclelist(), env.receipts, nil) 1188 if err != nil { 1189 return err 1190 } 1191 // If we're post merge, just ignore 1192 if !w.isTTDReached(block.Header()) { 1193 select { 1194 case w.taskCh <- &task{receipts: env.receipts, state: env.state, block: block, createdAt: time.Now()}: 1195 w.unconfirmed.Shift(block.NumberU64() - 1) 1196 1197 fees := totalFees(block, env.receipts) 1198 feesInEther := new(big.Float).Quo(new(big.Float).SetInt(fees), big.NewFloat(params.Ether)) 1199 log.Info("Commit new sealing work", "number", block.Number(), "sealhash", w.engine.SealHash(block.Header()), 1200 "uncles", len(env.uncles), "txs", env.tcount, 1201 "gas", block.GasUsed(), "fees", feesInEther, 1202 "elapsed", common.PrettyDuration(time.Since(start))) 1203 1204 case <-w.exitCh: 1205 log.Info("Worker has exited") 1206 } 1207 } 1208 } 1209 if update { 1210 w.updateSnapshot(env) 1211 } 1212 return nil 1213 } 1214 1215 // getSealingBlock generates the sealing block based on the given parameters. 1216 // The generation result will be passed back via the given channel no matter 1217 // the generation itself succeeds or not. 1218 func (w *worker) getSealingBlock(parent common.Hash, timestamp uint64, coinbase common.Address, random common.Hash, withdrawals types.Withdrawals, noTxs bool) (*types.Block, *big.Int, error) { 1219 req := &getWorkReq{ 1220 params: &generateParams{ 1221 timestamp: timestamp, 1222 forceTime: true, 1223 parentHash: parent, 1224 coinbase: coinbase, 1225 random: random, 1226 withdrawals: withdrawals, 1227 noUncle: true, 1228 noTxs: noTxs, 1229 }, 1230 result: make(chan *newPayloadResult, 1), 1231 } 1232 select { 1233 case w.getWorkCh <- req: 1234 result := <-req.result 1235 if result.err != nil { 1236 return nil, nil, result.err 1237 } 1238 return result.block, result.fees, nil 1239 case <-w.exitCh: 1240 return nil, nil, errors.New("miner closed") 1241 } 1242 } 1243 1244 // isTTDReached returns the indicator if the given block has reached the total 1245 // terminal difficulty for The Merge transition. 1246 func (w *worker) isTTDReached(header *types.Header) bool { 1247 td, ttd := w.chain.GetTd(header.ParentHash, header.Number.Uint64()-1), w.chain.Config().TerminalTotalDifficulty 1248 return td != nil && ttd != nil && td.Cmp(ttd) >= 0 1249 } 1250 1251 // copyReceipts makes a deep copy of the given receipts. 1252 func copyReceipts(receipts []*types.Receipt) []*types.Receipt { 1253 result := make([]*types.Receipt, len(receipts)) 1254 for i, l := range receipts { 1255 cpy := *l 1256 result[i] = &cpy 1257 } 1258 return result 1259 } 1260 1261 // postSideBlock fires a side chain event, only use it for testing. 1262 func (w *worker) postSideBlock(event core.ChainSideEvent) { 1263 select { 1264 case w.chainSideCh <- event: 1265 case <-w.exitCh: 1266 } 1267 } 1268 1269 // totalFees computes total consumed miner fees in Wei. Block transactions and receipts have to have the same order. 1270 func totalFees(block *types.Block, receipts []*types.Receipt) *big.Int { 1271 feesWei := new(big.Int) 1272 for i, tx := range block.Transactions() { 1273 minerFee, _ := tx.EffectiveGasTip(block.BaseFee()) 1274 feesWei.Add(feesWei, new(big.Int).Mul(new(big.Int).SetUint64(receipts[i].GasUsed), minerFee)) 1275 } 1276 return feesWei 1277 } 1278 1279 // signalToErr converts the interruption signal to a concrete error type for return. 1280 // The given signal must be a valid interruption signal. 1281 func signalToErr(signal int32) error { 1282 switch signal { 1283 case commitInterruptNewHead: 1284 return errBlockInterruptedByNewHead 1285 case commitInterruptResubmit: 1286 return errBlockInterruptedByRecommit 1287 case commitInterruptTimeout: 1288 return errBlockInterruptedByTimeout 1289 default: 1290 panic(fmt.Errorf("undefined signal %d", signal)) 1291 } 1292 }