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  }