github.com/ethereum-optimism/optimism/l2geth@v0.0.0-20230612200230-50b04ade19e3/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 "bytes" 21 "errors" 22 "fmt" 23 "math/big" 24 "sync" 25 "sync/atomic" 26 "time" 27 28 mapset "github.com/deckarep/golang-set" 29 "github.com/ethereum-optimism/optimism/l2geth/common" 30 "github.com/ethereum-optimism/optimism/l2geth/consensus" 31 "github.com/ethereum-optimism/optimism/l2geth/consensus/misc" 32 "github.com/ethereum-optimism/optimism/l2geth/core" 33 "github.com/ethereum-optimism/optimism/l2geth/core/state" 34 "github.com/ethereum-optimism/optimism/l2geth/core/types" 35 "github.com/ethereum-optimism/optimism/l2geth/event" 36 "github.com/ethereum-optimism/optimism/l2geth/log" 37 "github.com/ethereum-optimism/optimism/l2geth/metrics" 38 "github.com/ethereum-optimism/optimism/l2geth/params" 39 ) 40 41 const ( 42 // resultQueueSize is the size of channel listening to sealing result. 43 resultQueueSize = 10 44 45 // txChanSize is the size of channel listening to NewTxsEvent. 46 // The number is referenced from the size of tx pool. 47 txChanSize = 4096 48 49 // chainHeadChanSize is the size of channel listening to ChainHeadEvent. 50 chainHeadChanSize = 10 51 52 // chainSideChanSize is the size of channel listening to ChainSideEvent. 53 chainSideChanSize = 10 54 55 // resubmitAdjustChanSize is the size of resubmitting interval adjustment channel. 56 resubmitAdjustChanSize = 10 57 58 // miningLogAtDepth is the number of confirmations before logging successful mining. 59 miningLogAtDepth = 7 60 61 // minRecommitInterval is the minimal time interval to recreate the mining block with 62 // any newly arrived transactions. 63 minRecommitInterval = 1 * time.Second 64 65 // maxRecommitInterval is the maximum time interval to recreate the mining block with 66 // any newly arrived transactions. 67 maxRecommitInterval = 15 * time.Second 68 69 // intervalAdjustRatio is the impact a single interval adjustment has on sealing work 70 // resubmitting interval. 71 intervalAdjustRatio = 0.1 72 73 // intervalAdjustBias is applied during the new resubmit interval calculation in favor of 74 // increasing upper limit or decreasing lower limit so that the limit can be reachable. 75 intervalAdjustBias = 200 * 1000.0 * 1000.0 76 77 // staleThreshold is the maximum depth of the acceptable stale block. 78 staleThreshold = 7 79 ) 80 81 var ( 82 // ErrCannotCommitTxn signals that the transaction execution failed 83 // when attempting to mine a transaction. 84 // 85 // NOTE: This error is not expected to occur in regular operation of 86 // l2geth, rather the actual execution error should be returned to the 87 // user. 88 ErrCannotCommitTxn = errors.New("Cannot commit transaction in miner") 89 90 // rollup apply transaction metrics 91 accountReadTimer = metrics.NewRegisteredTimer("rollup/tx/account/reads", nil) 92 accountUpdateTimer = metrics.NewRegisteredTimer("rollup/tx/account/updates", nil) 93 storageReadTimer = metrics.NewRegisteredTimer("rollup/tx/storage/reads", nil) 94 storageUpdateTimer = metrics.NewRegisteredTimer("rollup/tx/storage/updates", nil) 95 txExecutionTimer = metrics.NewRegisteredTimer("rollup/tx/execution", nil) 96 ) 97 98 // environment is the worker's current environment and holds all of the current state information. 99 type environment struct { 100 signer types.Signer 101 102 state *state.StateDB // apply state changes here 103 ancestors mapset.Set // ancestor set (used for checking uncle parent validity) 104 family mapset.Set // family set (used for checking uncle invalidity) 105 uncles mapset.Set // uncle set 106 tcount int // tx count in cycle 107 gasPool *core.GasPool // available gas used to pack transactions 108 109 header *types.Header 110 txs []*types.Transaction 111 receipts []*types.Receipt 112 } 113 114 // task contains all information for consensus engine sealing and result submitting. 115 type task struct { 116 receipts []*types.Receipt 117 state *state.StateDB 118 block *types.Block 119 createdAt time.Time 120 } 121 122 const ( 123 commitInterruptNone int32 = iota 124 commitInterruptNewHead 125 commitInterruptResubmit 126 ) 127 128 // newWorkReq represents a request for new sealing work submitting with relative interrupt notifier. 129 type newWorkReq struct { 130 interrupt *int32 131 timestamp int64 132 } 133 134 // intervalAdjust represents a resubmitting interval adjustment. 135 type intervalAdjust struct { 136 ratio float64 137 inc bool 138 } 139 140 // worker is the main object which takes care of submitting new work to consensus engine 141 // and gathering the sealing result. 142 type worker struct { 143 config *Config 144 chainConfig *params.ChainConfig 145 engine consensus.Engine 146 eth Backend 147 chain *core.BlockChain 148 149 // Feeds 150 pendingLogsFeed event.Feed 151 152 // Subscriptions 153 mux *event.TypeMux 154 txsCh chan core.NewTxsEvent 155 txsSub event.Subscription 156 chainHeadCh chan core.ChainHeadEvent 157 chainHeadSub event.Subscription 158 chainSideCh chan core.ChainSideEvent 159 chainSideSub event.Subscription 160 rollupCh chan core.NewTxsEvent 161 rollupSub event.Subscription 162 163 // Channels 164 newWorkCh chan *newWorkReq 165 taskCh chan *task 166 resultCh chan *types.Block 167 startCh chan struct{} 168 exitCh chan struct{} 169 resubmitIntervalCh chan time.Duration 170 resubmitAdjustCh chan *intervalAdjust 171 172 current *environment // An environment for current running cycle. 173 localUncles map[common.Hash]*types.Block // A set of side blocks generated locally as the possible uncle blocks. 174 remoteUncles map[common.Hash]*types.Block // A set of side blocks as the possible uncle blocks. 175 unconfirmed *unconfirmedBlocks // A set of locally mined blocks pending canonicalness confirmations. 176 177 mu sync.RWMutex // The lock used to protect the coinbase and extra fields 178 coinbase common.Address 179 extra []byte 180 181 pendingMu sync.RWMutex 182 pendingTasks map[common.Hash]*task 183 184 snapshotMu sync.RWMutex // The lock used to protect the block snapshot and state snapshot 185 snapshotBlock *types.Block 186 snapshotState *state.StateDB 187 188 // atomic status counters 189 running int32 // The indicator whether the consensus engine is running or not. 190 newTxs int32 // New arrival transaction count since last sealing work submitting. 191 192 // External functions 193 isLocalBlock func(block *types.Block) bool // Function used to determine whether the specified block is mined by local miner. 194 195 // Test hooks 196 newTaskHook func(*task) // Method to call upon receiving a new sealing task. 197 skipSealHook func(*task) bool // Method to decide whether skipping the sealing. 198 fullTaskHook func() // Method to call before pushing the full sealing task. 199 resubmitHook func(time.Duration, time.Duration) // Method to call upon updating resubmitting interval. 200 } 201 202 func newWorker(config *Config, chainConfig *params.ChainConfig, engine consensus.Engine, eth Backend, mux *event.TypeMux, isLocalBlock func(*types.Block) bool, init bool) *worker { 203 worker := &worker{ 204 config: config, 205 chainConfig: chainConfig, 206 engine: engine, 207 eth: eth, 208 mux: mux, 209 chain: eth.BlockChain(), 210 isLocalBlock: isLocalBlock, 211 localUncles: make(map[common.Hash]*types.Block), 212 remoteUncles: make(map[common.Hash]*types.Block), 213 unconfirmed: newUnconfirmedBlocks(eth.BlockChain(), miningLogAtDepth), 214 pendingTasks: make(map[common.Hash]*task), 215 txsCh: make(chan core.NewTxsEvent, txChanSize), 216 rollupCh: make(chan core.NewTxsEvent, 1), 217 chainHeadCh: make(chan core.ChainHeadEvent, chainHeadChanSize), 218 chainSideCh: make(chan core.ChainSideEvent, chainSideChanSize), 219 newWorkCh: make(chan *newWorkReq), 220 taskCh: make(chan *task), 221 resultCh: make(chan *types.Block, resultQueueSize), 222 exitCh: make(chan struct{}), 223 startCh: make(chan struct{}, 1), 224 resubmitIntervalCh: make(chan time.Duration), 225 resubmitAdjustCh: make(chan *intervalAdjust, resubmitAdjustChanSize), 226 } 227 // Subscribe NewTxsEvent for tx pool 228 worker.txsSub = eth.TxPool().SubscribeNewTxsEvent(worker.txsCh) 229 // channel directly to the miner 230 worker.rollupSub = eth.SyncService().SubscribeNewTxsEvent(worker.rollupCh) 231 232 // Subscribe events for blockchain 233 worker.chainHeadSub = eth.BlockChain().SubscribeChainHeadEvent(worker.chainHeadCh) 234 worker.chainSideSub = eth.BlockChain().SubscribeChainSideEvent(worker.chainSideCh) 235 236 // Sanitize recommit interval if the user-specified one is too short. 237 recommit := worker.config.Recommit 238 if recommit < minRecommitInterval { 239 log.Warn("Sanitizing miner recommit interval", "provided", recommit, "updated", minRecommitInterval) 240 recommit = minRecommitInterval 241 } 242 243 go worker.mainLoop() 244 go worker.newWorkLoop(recommit) 245 go worker.resultLoop() 246 go worker.taskLoop() 247 248 // Submit first work to initialize pending state. 249 if init { 250 worker.startCh <- struct{}{} 251 } 252 return worker 253 } 254 255 // setEtherbase sets the etherbase used to initialize the block coinbase field. 256 func (w *worker) setEtherbase(addr common.Address) { 257 w.mu.Lock() 258 defer w.mu.Unlock() 259 w.coinbase = addr 260 } 261 262 // setExtra sets the content used to initialize the block extra field. 263 func (w *worker) setExtra(extra []byte) { 264 w.mu.Lock() 265 defer w.mu.Unlock() 266 w.extra = extra 267 } 268 269 // setRecommitInterval updates the interval for miner sealing work recommitting. 270 func (w *worker) setRecommitInterval(interval time.Duration) { 271 w.resubmitIntervalCh <- interval 272 } 273 274 // pending returns the pending state and corresponding block. 275 func (w *worker) pending() (*types.Block, *state.StateDB) { 276 // return a snapshot to avoid contention on currentMu mutex 277 w.snapshotMu.RLock() 278 defer w.snapshotMu.RUnlock() 279 if w.snapshotState == nil { 280 return nil, nil 281 } 282 return w.snapshotBlock, w.snapshotState.Copy() 283 } 284 285 // pendingBlock returns pending block. 286 func (w *worker) pendingBlock() *types.Block { 287 // return a snapshot to avoid contention on currentMu mutex 288 w.snapshotMu.RLock() 289 defer w.snapshotMu.RUnlock() 290 return w.snapshotBlock 291 } 292 293 // start sets the running status as 1 and triggers new work submitting. 294 func (w *worker) start() { 295 atomic.StoreInt32(&w.running, 1) 296 w.startCh <- struct{}{} 297 } 298 299 // stop sets the running status as 0. 300 func (w *worker) stop() { 301 atomic.StoreInt32(&w.running, 0) 302 } 303 304 // isRunning returns an indicator whether worker is running or not. 305 func (w *worker) isRunning() bool { 306 return atomic.LoadInt32(&w.running) == 1 307 } 308 309 // close terminates all background threads maintained by the worker. 310 // Note the worker does not support being closed multiple times. 311 func (w *worker) close() { 312 close(w.exitCh) 313 } 314 315 // newWorkLoop is a standalone goroutine to submit new mining work upon received events. 316 func (w *worker) newWorkLoop(recommit time.Duration) { 317 var ( 318 interrupt *int32 319 minRecommit = recommit // minimal resubmit interval specified by user. 320 timestamp int64 // timestamp for each round of mining. 321 ) 322 323 timer := time.NewTimer(0) 324 <-timer.C // discard the initial tick 325 326 // commit aborts in-flight transaction execution with given signal and resubmits a new one. 327 commit := func(s int32) { 328 if interrupt != nil { 329 atomic.StoreInt32(interrupt, s) 330 } 331 interrupt = new(int32) 332 w.newWorkCh <- &newWorkReq{interrupt: interrupt, timestamp: timestamp} 333 timer.Reset(recommit) 334 atomic.StoreInt32(&w.newTxs, 0) 335 } 336 // recalcRecommit recalculates the resubmitting interval upon feedback. 337 recalcRecommit := func(target float64, inc bool) { 338 var ( 339 prev = float64(recommit.Nanoseconds()) 340 next float64 341 ) 342 if inc { 343 next = prev*(1-intervalAdjustRatio) + intervalAdjustRatio*(target+intervalAdjustBias) 344 // Recap if interval is larger than the maximum time interval 345 if next > float64(maxRecommitInterval.Nanoseconds()) { 346 next = float64(maxRecommitInterval.Nanoseconds()) 347 } 348 } else { 349 next = prev*(1-intervalAdjustRatio) + intervalAdjustRatio*(target-intervalAdjustBias) 350 // Recap if interval is less than the user specified minimum 351 if next < float64(minRecommit.Nanoseconds()) { 352 next = float64(minRecommit.Nanoseconds()) 353 } 354 } 355 recommit = time.Duration(int64(next)) 356 } 357 // clearPending cleans the stale pending tasks. 358 clearPending := func(number uint64) { 359 w.pendingMu.Lock() 360 for h, t := range w.pendingTasks { 361 if t.block.NumberU64()+staleThreshold <= number { 362 delete(w.pendingTasks, h) 363 } 364 } 365 w.pendingMu.Unlock() 366 } 367 368 for { 369 select { 370 case <-w.startCh: 371 clearPending(w.chain.CurrentBlock().NumberU64()) 372 commit(commitInterruptNewHead) 373 374 // Remove this code for the OVM implementation. It is responsible for 375 // cleaning up memory with the call to `clearPending`, so be sure to 376 // call that in the new hot code path 377 /* 378 case <-w.chainHeadCh: 379 clearPending(head.Block.NumberU64()) 380 timestamp = time.Now().Unix() 381 commit(commitInterruptNewHead) 382 */ 383 384 case <-timer.C: 385 // If mining is running resubmit a new work cycle periodically to pull in 386 // higher priced transactions. Disable this overhead for pending blocks. 387 if w.isRunning() && (w.chainConfig.Clique == nil || w.chainConfig.Clique.Period > 0) { 388 // Short circuit if no new transaction arrives. 389 if atomic.LoadInt32(&w.newTxs) == 0 { 390 timer.Reset(recommit) 391 continue 392 } 393 commit(commitInterruptResubmit) 394 } 395 396 case interval := <-w.resubmitIntervalCh: 397 // Adjust resubmit interval explicitly by user. 398 if interval < minRecommitInterval { 399 log.Warn("Sanitizing miner recommit interval", "provided", interval, "updated", minRecommitInterval) 400 interval = minRecommitInterval 401 } 402 log.Info("Miner recommit interval update", "from", minRecommit, "to", interval) 403 minRecommit, recommit = interval, interval 404 405 if w.resubmitHook != nil { 406 w.resubmitHook(minRecommit, recommit) 407 } 408 409 case adjust := <-w.resubmitAdjustCh: 410 // Adjust resubmit interval by feedback. 411 if adjust.inc { 412 before := recommit 413 recalcRecommit(float64(recommit.Nanoseconds())/adjust.ratio, true) 414 log.Trace("Increase miner recommit interval", "from", before, "to", recommit) 415 } else { 416 before := recommit 417 recalcRecommit(float64(minRecommit.Nanoseconds()), false) 418 log.Trace("Decrease miner recommit interval", "from", before, "to", recommit) 419 } 420 421 if w.resubmitHook != nil { 422 w.resubmitHook(minRecommit, recommit) 423 } 424 425 case <-w.exitCh: 426 return 427 } 428 } 429 } 430 431 // mainLoop is a standalone goroutine to regenerate the sealing task based on the received event. 432 func (w *worker) mainLoop() { 433 defer w.txsSub.Unsubscribe() 434 defer w.chainHeadSub.Unsubscribe() 435 defer w.chainSideSub.Unsubscribe() 436 defer w.rollupSub.Unsubscribe() 437 438 for { 439 select { 440 case req := <-w.newWorkCh: 441 w.commitNewWork(req.interrupt, req.timestamp) 442 443 case ev := <-w.chainSideCh: 444 // Short circuit for duplicate side blocks 445 if _, exist := w.localUncles[ev.Block.Hash()]; exist { 446 continue 447 } 448 if _, exist := w.remoteUncles[ev.Block.Hash()]; exist { 449 continue 450 } 451 // Add side block to possible uncle block set depending on the author. 452 if w.isLocalBlock != nil && w.isLocalBlock(ev.Block) { 453 w.localUncles[ev.Block.Hash()] = ev.Block 454 } else { 455 w.remoteUncles[ev.Block.Hash()] = ev.Block 456 } 457 // If our mining block contains less than 2 uncle blocks, 458 // add the new uncle block if valid and regenerate a mining block. 459 if w.isRunning() && w.current != nil && w.current.uncles.Cardinality() < 2 { 460 start := time.Now() 461 if err := w.commitUncle(w.current, ev.Block.Header()); err == nil { 462 var uncles []*types.Header 463 w.current.uncles.Each(func(item interface{}) bool { 464 hash, ok := item.(common.Hash) 465 if !ok { 466 return false 467 } 468 uncle, exist := w.localUncles[hash] 469 if !exist { 470 uncle, exist = w.remoteUncles[hash] 471 } 472 if !exist { 473 return false 474 } 475 uncles = append(uncles, uncle.Header()) 476 return false 477 }) 478 w.commit(uncles, nil, start) 479 } 480 } 481 // Read from the sync service and mine single txs 482 // as they come. Wait for the block to be mined before 483 // reading the next tx from the channel when there is 484 // not an error processing the transaction. 485 case ev := <-w.rollupCh: 486 if len(ev.Txs) == 0 { 487 log.Warn("No transaction sent to miner from syncservice") 488 continue 489 } 490 tx := ev.Txs[0] 491 log.Debug("Attempting to commit rollup transaction", "hash", tx.Hash().Hex()) 492 // Build the block with the tx and add it to the chain. This will 493 // send the block through the `taskCh` and then through the 494 // `resultCh` which ultimately adds the block to the blockchain 495 // through `bc.WriteBlockWithState` 496 if err := w.commitNewTx(tx); err == nil { 497 // `chainHeadCh` is written to when a new block is added to the 498 // tip of the chain. Reading from the channel will block until 499 // the ethereum block is added to the chain downstream of `commitNewTx`. 500 // This will result in a deadlock if we call `commitNewTx` with 501 // a transaction that cannot be added to the chain, so this 502 // should be updated to a select statement that can also listen 503 // for errors. 504 head := <-w.chainHeadCh 505 txs := head.Block.Transactions() 506 if len(txs) == 0 { 507 log.Warn("No transactions in block") 508 continue 509 } 510 txn := txs[0] 511 height := head.Block.Number().Uint64() 512 log.Debug("Miner got new head", "height", height, "block-hash", head.Block.Hash().Hex(), "tx-hash", txn.Hash().Hex(), "tx-hash", tx.Hash().Hex()) 513 514 // Prevent memory leak by cleaning up pending tasks 515 // This is mostly copied from the `newWorkLoop` 516 // `clearPending` function and must be called 517 // periodically to clean up pending tasks. This 518 // function was originally called in `newWorkLoop` 519 // but the OVM implementation no longer uses that code path. 520 w.pendingMu.Lock() 521 for h := range w.pendingTasks { 522 delete(w.pendingTasks, h) 523 } 524 w.pendingMu.Unlock() 525 } else { 526 log.Error("Problem committing transaction", "msg", err) 527 if ev.ErrCh != nil { 528 ev.ErrCh <- err 529 } 530 } 531 532 case ev := <-w.txsCh: 533 // Apply transactions to the pending state if we're not mining. 534 // 535 // Note all transactions received may not be continuous with transactions 536 // already included in the current mining block. These transactions will 537 // be automatically eliminated. 538 if !w.isRunning() && w.current != nil { 539 // If block is already full, abort 540 if gp := w.current.gasPool; gp != nil && gp.Gas() < params.TxGas { 541 continue 542 } 543 w.mu.RLock() 544 coinbase := w.coinbase 545 w.mu.RUnlock() 546 547 txs := make(map[common.Address]types.Transactions) 548 for _, tx := range ev.Txs { 549 acc, _ := types.Sender(w.current.signer, tx) 550 txs[acc] = append(txs[acc], tx) 551 } 552 txset := types.NewTransactionsByPriceAndNonce(w.current.signer, txs) 553 tcount := w.current.tcount 554 w.commitTransactions(txset, coinbase, nil) 555 // Only update the snapshot if any new transactons were added 556 // to the pending block 557 if tcount != w.current.tcount { 558 w.updateSnapshot() 559 } 560 } else { 561 // If clique is running in dev mode(period is 0), disable 562 // advance sealing here. 563 if w.chainConfig.Clique != nil && w.chainConfig.Clique.Period == 0 { 564 w.commitNewWork(nil, time.Now().Unix()) 565 } 566 } 567 atomic.AddInt32(&w.newTxs, int32(len(ev.Txs))) 568 569 // System stopped 570 case <-w.exitCh: 571 return 572 case <-w.txsSub.Err(): 573 return 574 case <-w.chainHeadSub.Err(): 575 return 576 case <-w.chainSideSub.Err(): 577 return 578 } 579 } 580 } 581 582 // taskLoop is a standalone goroutine to fetch sealing task from the generator and 583 // push them to consensus engine. 584 func (w *worker) taskLoop() { 585 var ( 586 stopCh chan struct{} 587 prev common.Hash 588 ) 589 590 // interrupt aborts the in-flight sealing task. 591 interrupt := func() { 592 if stopCh != nil { 593 close(stopCh) 594 stopCh = nil 595 } 596 } 597 for { 598 select { 599 case task := <-w.taskCh: 600 if w.newTaskHook != nil { 601 w.newTaskHook(task) 602 } 603 // Reject duplicate sealing work due to resubmitting. 604 sealHash := w.engine.SealHash(task.block.Header()) 605 if sealHash == prev { 606 continue 607 } 608 // Interrupt previous sealing operation 609 interrupt() 610 stopCh, prev = make(chan struct{}), sealHash 611 612 if w.skipSealHook != nil && w.skipSealHook(task) { 613 continue 614 } 615 w.pendingMu.Lock() 616 w.pendingTasks[w.engine.SealHash(task.block.Header())] = task 617 w.pendingMu.Unlock() 618 619 if err := w.engine.Seal(w.chain, task.block, w.resultCh, stopCh); err != nil { 620 log.Warn("Block sealing failed", "err", err) 621 } 622 case <-w.exitCh: 623 interrupt() 624 return 625 } 626 } 627 } 628 629 // resultLoop is a standalone goroutine to handle sealing result submitting 630 // and flush relative data to the database. 631 func (w *worker) resultLoop() { 632 for { 633 select { 634 case block := <-w.resultCh: 635 // Short circuit when receiving empty result. 636 if block == nil { 637 continue 638 } 639 // Short circuit when receiving duplicate result caused by resubmitting. 640 if w.chain.HasBlock(block.Hash(), block.NumberU64()) { 641 continue 642 } 643 var ( 644 sealhash = w.engine.SealHash(block.Header()) 645 hash = block.Hash() 646 ) 647 w.pendingMu.RLock() 648 task, exist := w.pendingTasks[sealhash] 649 w.pendingMu.RUnlock() 650 if !exist { 651 log.Error("Block found but no relative pending task", "number", block.Number(), "sealhash", sealhash, "hash", hash) 652 continue 653 } 654 // Different block could share same sealhash, deep copy here to prevent write-write conflict. 655 var ( 656 receipts = make([]*types.Receipt, len(task.receipts)) 657 logs []*types.Log 658 ) 659 for i, receipt := range task.receipts { 660 // add block location fields 661 receipt.BlockHash = hash 662 receipt.BlockNumber = block.Number() 663 receipt.TransactionIndex = uint(i) 664 665 receipts[i] = new(types.Receipt) 666 *receipts[i] = *receipt 667 // Update the block hash in all logs since it is now available and not when the 668 // receipt/log of individual transactions were created. 669 for _, log := range receipt.Logs { 670 log.BlockHash = hash 671 } 672 logs = append(logs, receipt.Logs...) 673 } 674 // Commit block and state to database. 675 _, err := w.chain.WriteBlockWithState(block, receipts, logs, task.state, true) 676 if err != nil { 677 log.Error("Failed writing block to chain", "err", err) 678 continue 679 } 680 681 // Broadcast the block and announce chain insertion event 682 w.mux.Post(core.NewMinedBlockEvent{Block: block}) 683 684 // Insert the block into the set of pending ones to resultLoop for confirmations 685 w.unconfirmed.Insert(block.NumberU64(), block.Hash()) 686 687 case <-w.exitCh: 688 return 689 } 690 } 691 } 692 693 // makeCurrent creates a new environment for the current cycle. 694 func (w *worker) makeCurrent(parent *types.Block, header *types.Header) error { 695 state, err := w.chain.StateAt(parent.Root()) 696 if err != nil { 697 return err 698 } 699 env := &environment{ 700 signer: types.NewEIP155Signer(w.chainConfig.ChainID), 701 state: state, 702 ancestors: mapset.NewSet(), 703 family: mapset.NewSet(), 704 uncles: mapset.NewSet(), 705 header: header, 706 } 707 708 // when 08 is processed ancestors contain 07 (quick block) 709 for _, ancestor := range w.chain.GetBlocksFromHash(parent.Hash(), 7) { 710 for _, uncle := range ancestor.Uncles() { 711 env.family.Add(uncle.Hash()) 712 } 713 env.family.Add(ancestor.Hash()) 714 env.ancestors.Add(ancestor.Hash()) 715 } 716 717 // Keep track of transactions which return errors so they can be removed 718 env.tcount = 0 719 w.current = env 720 return nil 721 } 722 723 // commitUncle adds the given block to uncle block set, returns error if failed to add. 724 func (w *worker) commitUncle(env *environment, uncle *types.Header) error { 725 hash := uncle.Hash() 726 if env.uncles.Contains(hash) { 727 return errors.New("uncle not unique") 728 } 729 if env.header.ParentHash == uncle.ParentHash { 730 return errors.New("uncle is sibling") 731 } 732 if !env.ancestors.Contains(uncle.ParentHash) { 733 return errors.New("uncle's parent unknown") 734 } 735 if env.family.Contains(hash) { 736 return errors.New("uncle already included") 737 } 738 env.uncles.Add(uncle.Hash()) 739 return nil 740 } 741 742 // updateSnapshot updates pending snapshot block and state. 743 // Note this function assumes the current variable is thread safe. 744 func (w *worker) updateSnapshot() { 745 w.snapshotMu.Lock() 746 defer w.snapshotMu.Unlock() 747 748 var uncles []*types.Header 749 w.current.uncles.Each(func(item interface{}) bool { 750 hash, ok := item.(common.Hash) 751 if !ok { 752 return false 753 } 754 uncle, exist := w.localUncles[hash] 755 if !exist { 756 uncle, exist = w.remoteUncles[hash] 757 } 758 if !exist { 759 return false 760 } 761 uncles = append(uncles, uncle.Header()) 762 return false 763 }) 764 765 w.snapshotBlock = types.NewBlock( 766 w.current.header, 767 w.current.txs, 768 uncles, 769 w.current.receipts, 770 ) 771 772 w.snapshotState = w.current.state.Copy() 773 } 774 775 func (w *worker) commitTransaction(tx *types.Transaction, coinbase common.Address) ([]*types.Log, error) { 776 // Make sure there's only one tx per block 777 if w.current != nil && len(w.current.txs) > 0 { 778 return nil, core.ErrGasLimitReached 779 } 780 snap := w.current.state.Snapshot() 781 782 start := time.Now() 783 receipt, err := core.ApplyTransaction(w.chainConfig, w.chain, &coinbase, w.current.gasPool, w.current.state, w.current.header, tx, &w.current.header.GasUsed, *w.chain.GetVMConfig()) 784 if err != nil { 785 w.current.state.RevertToSnapshot(snap) 786 return nil, err 787 } 788 w.current.txs = append(w.current.txs, tx) 789 w.current.receipts = append(w.current.receipts, receipt) 790 791 updateTransactionStateMetrics(start, w.current.state) 792 793 return receipt.Logs, nil 794 } 795 796 func (w *worker) commitTransactions(txs *types.TransactionsByPriceAndNonce, coinbase common.Address, interrupt *int32) bool { 797 return w.commitTransactionsWithError(txs, coinbase, interrupt) != nil 798 } 799 800 func (w *worker) commitTransactionsWithError(txs *types.TransactionsByPriceAndNonce, coinbase common.Address, interrupt *int32) error { 801 // Short circuit if current is nil 802 if w.current == nil { 803 return ErrCannotCommitTxn 804 } 805 806 if w.current.gasPool == nil { 807 w.current.gasPool = new(core.GasPool).AddGas(w.current.header.GasLimit) 808 } 809 810 var coalescedLogs []*types.Log 811 812 for { 813 // In the following three cases, we will interrupt the execution of the transaction. 814 // (1) new head block event arrival, the interrupt signal is 1 815 // (2) worker start or restart, the interrupt signal is 1 816 // (3) worker recreate the mining block with any newly arrived transactions, the interrupt signal is 2. 817 // For the first two cases, the semi-finished work will be discarded. 818 // For the third case, the semi-finished work will be submitted to the consensus engine. 819 if interrupt != nil && atomic.LoadInt32(interrupt) != commitInterruptNone { 820 // Notify resubmit loop to increase resubmitting interval due to too frequent commits. 821 if atomic.LoadInt32(interrupt) == commitInterruptResubmit { 822 ratio := float64(w.current.header.GasLimit-w.current.gasPool.Gas()) / float64(w.current.header.GasLimit) 823 if ratio < 0.1 { 824 ratio = 0.1 825 } 826 w.resubmitAdjustCh <- &intervalAdjust{ 827 ratio: ratio, 828 inc: true, 829 } 830 } 831 if w.current.tcount == 0 || 832 atomic.LoadInt32(interrupt) == commitInterruptNewHead { 833 return ErrCannotCommitTxn 834 } 835 return nil 836 } 837 // If we don't have enough gas for any further transactions then we're done 838 if w.current.gasPool.Gas() < params.TxGas { 839 log.Trace("Not enough gas for further transactions", "have", w.current.gasPool, "want", params.TxGas) 840 break 841 } 842 // Retrieve the next transaction and abort if all done 843 tx := txs.Peek() 844 if tx == nil { 845 break 846 } 847 848 // Error may be ignored here. The error has already been checked 849 // during transaction acceptance is the transaction pool. 850 // 851 // We use the eip155 signer regardless of the current hf. 852 from, _ := types.Sender(w.current.signer, tx) 853 // Check whether the tx is replay protected. If we're not in the EIP155 hf 854 // phase, start ignoring the sender until we do. 855 if tx.Protected() && !w.chainConfig.IsEIP155(w.current.header.Number) { 856 log.Trace("Ignoring reply protected transaction", "hash", tx.Hash(), "eip155", w.chainConfig.EIP155Block) 857 858 txs.Pop() 859 continue 860 } 861 // Start executing the transaction 862 w.current.state.Prepare(tx.Hash(), common.Hash{}, w.current.tcount) 863 864 logs, err := w.commitTransaction(tx, coinbase) 865 switch err { 866 case core.ErrGasLimitReached: 867 // Pop the current out-of-gas transaction without shifting in the next from the account 868 log.Trace("Gas limit exceeded for current block", "sender", from) 869 txs.Pop() 870 871 case core.ErrNonceTooLow: 872 // New head notification data race between the transaction pool and miner, shift 873 log.Trace("Skipping transaction with low nonce", "sender", from, "nonce", tx.Nonce()) 874 txs.Shift() 875 876 case core.ErrNonceTooHigh: 877 // Reorg notification data race between the transaction pool and miner, skip account = 878 log.Trace("Skipping account with high nonce", "sender", from, "nonce", tx.Nonce()) 879 txs.Pop() 880 881 case nil: 882 // Everything ok, collect the logs and shift in the next transaction from the same account 883 coalescedLogs = append(coalescedLogs, logs...) 884 w.current.tcount++ 885 txs.Shift() 886 887 default: 888 // Strange error, discard the transaction and get the next in line (note, the 889 // nonce-too-high clause will prevent us from executing in vain). 890 log.Debug("Transaction failed, account skipped", "hash", tx.Hash(), "err", err) 891 txs.Shift() 892 } 893 894 // UsingOVM 895 // Return specific execution errors directly to the user to 896 // avoid returning the generic ErrCannotCommitTxnErr. It is safe 897 // to return the error directly since l2geth only processes at 898 // most one transaction per block. 899 if err != nil { 900 return err 901 } 902 } 903 904 if !w.isRunning() && len(coalescedLogs) > 0 { 905 // We don't push the pendingLogsEvent while we are mining. The reason is that 906 // when we are mining, the worker will regenerate a mining block every 3 seconds. 907 // In order to avoid pushing the repeated pendingLog, we disable the pending log pushing. 908 909 // make a copy, the state caches the logs and these logs get "upgraded" from pending to mined 910 // logs by filling in the block hash when the block was mined by the local miner. This can 911 // cause a race condition if a log was "upgraded" before the PendingLogsEvent is processed. 912 cpy := make([]*types.Log, len(coalescedLogs)) 913 for i, l := range coalescedLogs { 914 cpy[i] = new(types.Log) 915 *cpy[i] = *l 916 } 917 w.pendingLogsFeed.Send(cpy) 918 } 919 // Notify resubmit loop to decrease resubmitting interval if current interval is larger 920 // than the user-specified one. 921 if interrupt != nil { 922 w.resubmitAdjustCh <- &intervalAdjust{inc: false} 923 } 924 if w.current.tcount == 0 { 925 return ErrCannotCommitTxn 926 } 927 return nil 928 } 929 930 // commitNewTx is an OVM addition that mines a block with a single tx in it. 931 // It needs to return an error in the case there is an error to prevent waiting 932 // on reading from a channel that is written to when a new block is added to the 933 // chain. 934 func (w *worker) commitNewTx(tx *types.Transaction) error { 935 w.mu.RLock() 936 defer w.mu.RUnlock() 937 tstart := time.Now() 938 939 parent := w.chain.CurrentBlock() 940 num := parent.Number() 941 942 // Preserve liveliness as best as possible. Must panic on L1 to L2 943 // transactions as the timestamp cannot be malleated 944 if parent.Time() > tx.L1Timestamp() { 945 log.Error("Monotonicity violation", "index", num, "parent", parent.Time(), "tx", tx.L1Timestamp()) 946 } 947 948 // Fill in the index field in the tx meta if it is `nil`. 949 // This should only ever happen in the case of the sequencer 950 // receiving a queue origin sequencer transaction. The verifier 951 // should always receive transactions with an index as they 952 // have already been confirmed in the canonical transaction chain. 953 // Use the parent's block number because the CTC is 0 indexed. 954 if meta := tx.GetMeta(); meta.Index == nil { 955 index := num.Uint64() 956 meta.Index = &index 957 tx.SetTransactionMeta(meta) 958 } 959 header := &types.Header{ 960 ParentHash: parent.Hash(), 961 Number: new(big.Int).Add(num, common.Big1), 962 GasLimit: w.config.GasFloor, 963 Extra: w.extra, 964 Time: tx.L1Timestamp(), 965 } 966 if err := w.engine.Prepare(w.chain, header); err != nil { 967 return fmt.Errorf("Failed to prepare header for mining: %w", err) 968 } 969 // Could potentially happen if starting to mine in an odd state. 970 err := w.makeCurrent(parent, header) 971 if err != nil { 972 return fmt.Errorf("Failed to create mining context: %w", err) 973 } 974 transactions := make(map[common.Address]types.Transactions) 975 acc, _ := types.Sender(w.current.signer, tx) 976 transactions[acc] = types.Transactions{tx} 977 txs := types.NewTransactionsByPriceAndNonce(w.current.signer, transactions) 978 if err := w.commitTransactionsWithError(txs, w.coinbase, nil); err != nil { 979 return err 980 } 981 return w.commit(nil, w.fullTaskHook, tstart) 982 } 983 984 // commitNewWork generates several new sealing tasks based on the parent block. 985 func (w *worker) commitNewWork(interrupt *int32, timestamp int64) { 986 w.mu.RLock() 987 defer w.mu.RUnlock() 988 989 tstart := time.Now() 990 parent := w.chain.CurrentBlock() 991 992 num := parent.Number() 993 header := &types.Header{ 994 ParentHash: parent.Hash(), 995 Number: num.Add(num, common.Big1), 996 GasLimit: w.config.GasFloor, 997 Extra: w.extra, 998 Time: uint64(timestamp), 999 } 1000 // Only set the coinbase if our consensus engine is running (avoid spurious block rewards) 1001 if w.isRunning() { 1002 if w.coinbase == (common.Address{}) { 1003 log.Error("Refusing to mine without etherbase") 1004 return 1005 } 1006 header.Coinbase = w.coinbase 1007 } 1008 if err := w.engine.Prepare(w.chain, header); err != nil { 1009 log.Error("Failed to prepare header for mining", "err", err) 1010 return 1011 } 1012 // If we are care about TheDAO hard-fork check whether to override the extra-data or not 1013 if daoBlock := w.chainConfig.DAOForkBlock; daoBlock != nil { 1014 // Check whether the block is among the fork extra-override range 1015 limit := new(big.Int).Add(daoBlock, params.DAOForkExtraRange) 1016 if header.Number.Cmp(daoBlock) >= 0 && header.Number.Cmp(limit) < 0 { 1017 // Depending whether we support or oppose the fork, override differently 1018 if w.chainConfig.DAOForkSupport { 1019 header.Extra = common.CopyBytes(params.DAOForkBlockExtra) 1020 } else if bytes.Equal(header.Extra, params.DAOForkBlockExtra) { 1021 header.Extra = []byte{} // If miner opposes, don't let it use the reserved extra-data 1022 } 1023 } 1024 } 1025 // Could potentially happen if starting to mine in an odd state. 1026 err := w.makeCurrent(parent, header) 1027 if err != nil { 1028 log.Error("Failed to create mining context", "err", err) 1029 return 1030 } 1031 // Create the current work task and check any fork transitions needed 1032 env := w.current 1033 if w.chainConfig.DAOForkSupport && w.chainConfig.DAOForkBlock != nil && w.chainConfig.DAOForkBlock.Cmp(header.Number) == 0 { 1034 misc.ApplyDAOHardFork(env.state) 1035 } 1036 // Accumulate the uncles for the current block 1037 uncles := make([]*types.Header, 0, 2) 1038 commitUncles := func(blocks map[common.Hash]*types.Block) { 1039 // Clean up stale uncle blocks first 1040 for hash, uncle := range blocks { 1041 if uncle.NumberU64()+staleThreshold <= header.Number.Uint64() { 1042 delete(blocks, hash) 1043 } 1044 } 1045 for hash, uncle := range blocks { 1046 if len(uncles) == 2 { 1047 break 1048 } 1049 if err := w.commitUncle(env, uncle.Header()); err != nil { 1050 log.Trace("Possible uncle rejected", "hash", hash, "reason", err) 1051 } else { 1052 log.Debug("Committing new uncle to block", "hash", hash) 1053 uncles = append(uncles, uncle.Header()) 1054 } 1055 } 1056 } 1057 // Prefer to locally generated uncle 1058 commitUncles(w.localUncles) 1059 commitUncles(w.remoteUncles) 1060 1061 // Fill the block with all available pending transactions. 1062 pending, err := w.eth.TxPool().Pending() 1063 if err != nil { 1064 log.Error("Failed to fetch pending transactions", "err", err) 1065 return 1066 } 1067 // Short circuit if there is no available pending transactions 1068 if len(pending) == 0 { 1069 w.updateSnapshot() 1070 return 1071 } 1072 // Split the pending transactions into locals and remotes 1073 localTxs, remoteTxs := make(map[common.Address]types.Transactions), pending 1074 for _, account := range w.eth.TxPool().Locals() { 1075 if txs := remoteTxs[account]; len(txs) > 0 { 1076 delete(remoteTxs, account) 1077 localTxs[account] = txs 1078 } 1079 } 1080 if len(localTxs) > 0 { 1081 txs := types.NewTransactionsByPriceAndNonce(w.current.signer, localTxs) 1082 if w.commitTransactions(txs, w.coinbase, interrupt) { 1083 return 1084 } 1085 } 1086 if len(remoteTxs) > 0 { 1087 txs := types.NewTransactionsByPriceAndNonce(w.current.signer, remoteTxs) 1088 if w.commitTransactions(txs, w.coinbase, interrupt) { 1089 return 1090 } 1091 } 1092 w.commit(uncles, w.fullTaskHook, tstart) 1093 } 1094 1095 // commit runs any post-transaction state modifications, assembles the final block 1096 // and commits new work if consensus engine is running. 1097 func (w *worker) commit(uncles []*types.Header, interval func(), start time.Time) error { 1098 // Deep copy receipts here to avoid interaction between different tasks. 1099 receipts := make([]*types.Receipt, len(w.current.receipts)) 1100 for i, l := range w.current.receipts { 1101 receipts[i] = new(types.Receipt) 1102 *receipts[i] = *l 1103 } 1104 s := w.current.state.Copy() 1105 block, err := w.engine.FinalizeAndAssemble(w.chain, w.current.header, s, w.current.txs, uncles, w.current.receipts) 1106 if err != nil { 1107 return err 1108 } 1109 1110 // As a sanity check, ensure all new blocks have exactly one 1111 // transaction. This check is done here just in case any of our 1112 // higher-evel checks failed to catch empty blocks passed to commit. 1113 txs := block.Transactions() 1114 if len(txs) != 1 { 1115 return fmt.Errorf("Block created with %d transactions rather than 1 at %d", len(txs), block.NumberU64()) 1116 } 1117 1118 if w.isRunning() { 1119 if interval != nil { 1120 interval() 1121 } 1122 // Writing to the taskCh will result in the block being added to the 1123 // chain via the resultCh 1124 select { 1125 case w.taskCh <- &task{receipts: receipts, state: s, block: block, createdAt: time.Now()}: 1126 w.unconfirmed.Shift(block.NumberU64() - 1) 1127 1128 feesWei := new(big.Int) 1129 for i, tx := range block.Transactions() { 1130 feesWei.Add(feesWei, new(big.Int).Mul(new(big.Int).SetUint64(receipts[i].GasUsed), tx.GasPrice())) 1131 } 1132 feesEth := new(big.Float).Quo(new(big.Float).SetInt(feesWei), new(big.Float).SetInt(big.NewInt(params.Ether))) 1133 1134 tx := txs[0] 1135 bn := tx.L1BlockNumber() 1136 if bn == nil { 1137 bn = new(big.Int) 1138 } 1139 log.Info("New block", "index", block.Number().Uint64()-uint64(1), "l1-timestamp", tx.L1Timestamp(), "l1-blocknumber", bn.Uint64(), "tx-hash", tx.Hash().Hex(), 1140 "queue-orign", tx.QueueOrigin(), "gas", block.GasUsed(), "fees", feesEth, "elapsed", common.PrettyDuration(time.Since(start))) 1141 1142 case <-w.exitCh: 1143 log.Info("Worker has exited") 1144 } 1145 } 1146 w.updateSnapshot() 1147 return nil 1148 } 1149 1150 // postSideBlock fires a side chain event, only use it for testing. 1151 func (w *worker) postSideBlock(event core.ChainSideEvent) { 1152 select { 1153 case w.chainSideCh <- event: 1154 case <-w.exitCh: 1155 } 1156 } 1157 1158 func updateTransactionStateMetrics(start time.Time, state *state.StateDB) { 1159 accountReadTimer.Update(state.AccountReads) 1160 storageReadTimer.Update(state.StorageReads) 1161 accountUpdateTimer.Update(state.AccountUpdates) 1162 storageUpdateTimer.Update(state.StorageUpdates) 1163 1164 triehash := state.AccountHashes + state.StorageHashes 1165 txExecutionTimer.Update(time.Since(start) - triehash) 1166 }