github.com/klaytn/klaytn@v1.10.2/work/worker.go (about)

     1  // Modifications Copyright 2018 The klaytn Authors
     2  // Copyright 2015 The go-ethereum Authors
     3  // This file is part of the go-ethereum library.
     4  //
     5  // The go-ethereum library is free software: you can redistribute it and/or modify
     6  // it under the terms of the GNU Lesser General Public License as published by
     7  // the Free Software Foundation, either version 3 of the License, or
     8  // (at your option) any later version.
     9  //
    10  // The go-ethereum library is distributed in the hope that it will be useful,
    11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    13  // GNU Lesser General Public License for more details.
    14  //
    15  // You should have received a copy of the GNU Lesser General Public License
    16  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    17  //
    18  // This file is derived from miner/worker.go (2018/06/04).
    19  // Modified and improved for the klaytn development.
    20  
    21  package work
    22  
    23  import (
    24  	"math/big"
    25  	"sync"
    26  	"sync/atomic"
    27  	"time"
    28  
    29  	"github.com/klaytn/klaytn/blockchain"
    30  	"github.com/klaytn/klaytn/blockchain/state"
    31  	"github.com/klaytn/klaytn/blockchain/types"
    32  	"github.com/klaytn/klaytn/blockchain/vm"
    33  	"github.com/klaytn/klaytn/common"
    34  	"github.com/klaytn/klaytn/consensus"
    35  	"github.com/klaytn/klaytn/consensus/misc"
    36  	"github.com/klaytn/klaytn/event"
    37  	klaytnmetrics "github.com/klaytn/klaytn/metrics"
    38  	"github.com/klaytn/klaytn/params"
    39  	"github.com/klaytn/klaytn/reward"
    40  	"github.com/klaytn/klaytn/storage/database"
    41  	"github.com/rcrowley/go-metrics"
    42  )
    43  
    44  const (
    45  	resultQueueSize  = 10
    46  	miningLogAtDepth = 5
    47  
    48  	// txChanSize is the size of channel listening to NewTxsEvent.
    49  	// The number is referenced from the size of tx pool.
    50  	txChanSize = 4096
    51  	// chainHeadChanSize is the size of channel listening to ChainHeadEvent.
    52  	chainHeadChanSize = 10
    53  	// chainSideChanSize is the size of channel listening to ChainSideEvent.
    54  	chainSideChanSize = 10
    55  	// maxResendSize is the size of resending transactions to peer in order to prevent the txs from missing.
    56  	maxResendTxSize = 1000
    57  )
    58  
    59  var (
    60  	// Metrics for miner
    61  	timeLimitReachedCounter = metrics.NewRegisteredCounter("miner/timelimitreached", nil)
    62  	tooLongTxCounter        = metrics.NewRegisteredCounter("miner/toolongtx", nil)
    63  	ResultChGauge           = metrics.NewRegisteredGauge("miner/resultch", nil)
    64  	resentTxGauge           = metrics.NewRegisteredGauge("miner/tx/resend/gauge", nil)
    65  	usedAllTxsCounter       = metrics.NewRegisteredCounter("miner/usedalltxs", nil)
    66  	checkedTxsGauge         = metrics.NewRegisteredGauge("miner/checkedtxs", nil)
    67  	tCountGauge             = metrics.NewRegisteredGauge("miner/tcount", nil)
    68  	nonceTooLowTxsGauge     = metrics.NewRegisteredGauge("miner/nonce/low/txs", nil)
    69  	nonceTooHighTxsGauge    = metrics.NewRegisteredGauge("miner/nonce/high/txs", nil)
    70  	gasLimitReachedTxsGauge = metrics.NewRegisteredGauge("miner/limitreached/gas/txs", nil)
    71  	strangeErrorTxsCounter  = metrics.NewRegisteredCounter("miner/strangeerror/txs", nil)
    72  
    73  	blockBaseFee              = metrics.NewRegisteredGauge("miner/block/mining/basefee", nil)
    74  	blockMiningTimer          = klaytnmetrics.NewRegisteredHybridTimer("miner/block/mining/time", nil)
    75  	blockMiningExecuteTxTimer = klaytnmetrics.NewRegisteredHybridTimer("miner/block/execute/time", nil)
    76  	blockMiningCommitTxTimer  = klaytnmetrics.NewRegisteredHybridTimer("miner/block/commit/time", nil)
    77  	blockMiningFinalizeTimer  = klaytnmetrics.NewRegisteredHybridTimer("miner/block/finalize/time", nil)
    78  
    79  	accountReadTimer   = klaytnmetrics.NewRegisteredHybridTimer("miner/block/account/reads", nil)
    80  	accountHashTimer   = klaytnmetrics.NewRegisteredHybridTimer("miner/block/account/hashes", nil)
    81  	accountUpdateTimer = klaytnmetrics.NewRegisteredHybridTimer("miner/block/account/updates", nil)
    82  	accountCommitTimer = klaytnmetrics.NewRegisteredHybridTimer("miner/block/account/commits", nil)
    83  
    84  	storageReadTimer   = klaytnmetrics.NewRegisteredHybridTimer("miner/block/storage/reads", nil)
    85  	storageHashTimer   = klaytnmetrics.NewRegisteredHybridTimer("miner/block/storage/hashes", nil)
    86  	storageUpdateTimer = klaytnmetrics.NewRegisteredHybridTimer("miner/block/storage/updates", nil)
    87  	storageCommitTimer = klaytnmetrics.NewRegisteredHybridTimer("miner/block/storage/commits", nil)
    88  
    89  	snapshotAccountReadTimer = metrics.NewRegisteredTimer("miner/snapshot/account/reads", nil)
    90  	snapshotStorageReadTimer = metrics.NewRegisteredTimer("miner/snapshot/storage/reads", nil)
    91  	snapshotCommitTimer      = metrics.NewRegisteredTimer("miner/snapshot/commits", nil)
    92  	calcDeferredRewardTimer  = metrics.NewRegisteredTimer("reward/distribute/calcdeferredreward", nil)
    93  )
    94  
    95  // Agent can register themself with the worker
    96  type Agent interface {
    97  	Work() chan<- *Task
    98  	SetReturnCh(chan<- *Result)
    99  	Stop()
   100  	Start()
   101  	GetHashRate() int64
   102  }
   103  
   104  // Task is the workers current environment and holds
   105  // all of the current state information
   106  type Task struct {
   107  	config *params.ChainConfig
   108  	signer types.Signer
   109  
   110  	stateMu sync.RWMutex   // protects state
   111  	state   *state.StateDB // apply state changes here
   112  	tcount  int            // tx count in cycle
   113  
   114  	Block *types.Block // the new block
   115  
   116  	header   *types.Header
   117  	txs      []*types.Transaction
   118  	receipts []*types.Receipt
   119  
   120  	createdAt time.Time
   121  }
   122  
   123  type Result struct {
   124  	Task  *Task
   125  	Block *types.Block
   126  }
   127  
   128  // worker is the main object which takes care of applying messages to the new state
   129  type worker struct {
   130  	config *params.ChainConfig
   131  	engine consensus.Engine
   132  
   133  	mu sync.Mutex
   134  
   135  	// update loop
   136  	mux          *event.TypeMux
   137  	txsCh        chan blockchain.NewTxsEvent
   138  	txsSub       event.Subscription
   139  	chainHeadCh  chan blockchain.ChainHeadEvent
   140  	chainHeadSub event.Subscription
   141  	chainSideCh  chan blockchain.ChainSideEvent
   142  	chainSideSub event.Subscription
   143  	wg           sync.WaitGroup
   144  
   145  	agents map[Agent]struct{}
   146  	recv   chan *Result
   147  
   148  	backend Backend
   149  	chain   BlockChain
   150  	proc    blockchain.Validator
   151  	chainDB database.DBManager
   152  
   153  	extra []byte
   154  
   155  	currentMu  sync.Mutex
   156  	current    *Task
   157  	rewardbase common.Address
   158  
   159  	snapshotMu    sync.RWMutex
   160  	snapshotBlock *types.Block
   161  	snapshotState *state.StateDB
   162  
   163  	// atomic status counters
   164  	mining int32
   165  	atWork int32
   166  
   167  	nodetype common.ConnType
   168  }
   169  
   170  func newWorker(config *params.ChainConfig, engine consensus.Engine, rewardbase common.Address, backend Backend, mux *event.TypeMux, nodetype common.ConnType, TxResendUseLegacy bool) *worker {
   171  	worker := &worker{
   172  		config:      config,
   173  		engine:      engine,
   174  		backend:     backend,
   175  		mux:         mux,
   176  		txsCh:       make(chan blockchain.NewTxsEvent, txChanSize),
   177  		chainHeadCh: make(chan blockchain.ChainHeadEvent, chainHeadChanSize),
   178  		chainSideCh: make(chan blockchain.ChainSideEvent, chainSideChanSize),
   179  		chainDB:     backend.ChainDB(),
   180  		recv:        make(chan *Result, resultQueueSize),
   181  		chain:       backend.BlockChain(),
   182  		proc:        backend.BlockChain().Validator(),
   183  		agents:      make(map[Agent]struct{}),
   184  		nodetype:    nodetype,
   185  		rewardbase:  rewardbase,
   186  	}
   187  
   188  	// Subscribe NewTxsEvent for tx pool
   189  	worker.txsSub = backend.TxPool().SubscribeNewTxsEvent(worker.txsCh)
   190  	// Subscribe events for blockchain
   191  	worker.chainHeadSub = backend.BlockChain().SubscribeChainHeadEvent(worker.chainHeadCh)
   192  	worker.chainSideSub = backend.BlockChain().SubscribeChainSideEvent(worker.chainSideCh)
   193  	go worker.update()
   194  
   195  	go worker.wait(TxResendUseLegacy)
   196  	return worker
   197  }
   198  
   199  func (self *worker) setExtra(extra []byte) {
   200  	self.mu.Lock()
   201  	defer self.mu.Unlock()
   202  	self.extra = extra
   203  }
   204  
   205  func (self *worker) pending() (*types.Block, *state.StateDB) {
   206  	if atomic.LoadInt32(&self.mining) == 0 {
   207  		// return a snapshot to avoid contention on currentMu mutex
   208  		self.snapshotMu.RLock()
   209  		defer self.snapshotMu.RUnlock()
   210  		return self.snapshotBlock, self.snapshotState.Copy()
   211  	}
   212  
   213  	self.currentMu.Lock()
   214  	defer self.currentMu.Unlock()
   215  	self.current.stateMu.Lock()
   216  	defer self.current.stateMu.Unlock()
   217  	return self.current.Block, self.current.state.Copy()
   218  }
   219  
   220  func (self *worker) pendingBlock() *types.Block {
   221  	if atomic.LoadInt32(&self.mining) == 0 {
   222  		// return a snapshot to avoid contention on currentMu mutex
   223  		self.snapshotMu.RLock()
   224  		defer self.snapshotMu.RUnlock()
   225  		return self.snapshotBlock
   226  	}
   227  
   228  	self.currentMu.Lock()
   229  	defer self.currentMu.Unlock()
   230  	return self.current.Block
   231  }
   232  
   233  func (self *worker) start() {
   234  	self.mu.Lock()
   235  	defer self.mu.Unlock()
   236  
   237  	atomic.StoreInt32(&self.mining, 1)
   238  
   239  	// istanbul BFT
   240  	if istanbul, ok := self.engine.(consensus.Istanbul); ok {
   241  		istanbul.Start(self.chain, self.chain.CurrentBlock, self.chain.HasBadBlock)
   242  	}
   243  
   244  	// spin up agents
   245  	for agent := range self.agents {
   246  		agent.Start()
   247  	}
   248  }
   249  
   250  func (self *worker) stop() {
   251  	self.wg.Wait()
   252  
   253  	self.mu.Lock()
   254  	defer self.mu.Unlock()
   255  	if atomic.LoadInt32(&self.mining) == 1 {
   256  		for agent := range self.agents {
   257  			agent.Stop()
   258  		}
   259  	}
   260  
   261  	// istanbul BFT
   262  	if istanbul, ok := self.engine.(consensus.Istanbul); ok {
   263  		istanbul.Stop()
   264  	}
   265  
   266  	atomic.StoreInt32(&self.mining, 0)
   267  	atomic.StoreInt32(&self.atWork, 0)
   268  }
   269  
   270  func (self *worker) register(agent Agent) {
   271  	self.mu.Lock()
   272  	defer self.mu.Unlock()
   273  	self.agents[agent] = struct{}{}
   274  	agent.SetReturnCh(self.recv)
   275  }
   276  
   277  func (self *worker) unregister(agent Agent) {
   278  	self.mu.Lock()
   279  	defer self.mu.Unlock()
   280  	delete(self.agents, agent)
   281  	agent.Stop()
   282  }
   283  
   284  func (self *worker) handleTxsCh(quitByErr chan bool) {
   285  	defer self.txsSub.Unsubscribe()
   286  
   287  	for {
   288  		select {
   289  		// Handle NewTxsEvent
   290  		case <-self.txsCh:
   291  			if atomic.LoadInt32(&self.mining) != 0 {
   292  				// If we're mining, but nothing is being processed, wake on new transactions
   293  				if self.config.Clique != nil && self.config.Clique.Period == 0 {
   294  					self.commitNewWork()
   295  				}
   296  			}
   297  
   298  		case <-quitByErr:
   299  			return
   300  		}
   301  	}
   302  }
   303  
   304  func (self *worker) update() {
   305  	defer self.chainHeadSub.Unsubscribe()
   306  	defer self.chainSideSub.Unsubscribe()
   307  
   308  	quitByErr := make(chan bool, 1)
   309  	go self.handleTxsCh(quitByErr)
   310  
   311  	for {
   312  		// A real event arrived, process interesting content
   313  		select {
   314  		// Handle ChainHeadEvent
   315  		case <-self.chainHeadCh:
   316  			// istanbul BFT
   317  			if h, ok := self.engine.(consensus.Handler); ok {
   318  				h.NewChainHead()
   319  			}
   320  			self.commitNewWork()
   321  
   322  			// TODO-Klaytn-Issue264 If we are using istanbul BFT, then we always have a canonical chain.
   323  			//         Later we may be able to refine below code.
   324  			// Handle ChainSideEvent
   325  		case <-self.chainSideCh:
   326  
   327  			// System stopped
   328  		case <-self.txsSub.Err():
   329  			quitByErr <- true
   330  			return
   331  		case <-self.chainHeadSub.Err():
   332  			quitByErr <- true
   333  			return
   334  		case <-self.chainSideSub.Err():
   335  			quitByErr <- true
   336  			return
   337  		}
   338  	}
   339  }
   340  
   341  func (self *worker) wait(TxResendUseLegacy bool) {
   342  	for {
   343  		mustCommitNewWork := true
   344  		for result := range self.recv {
   345  			atomic.AddInt32(&self.atWork, -1)
   346  			ResultChGauge.Update(ResultChGauge.Value() - 1)
   347  			if result == nil {
   348  				continue
   349  			}
   350  
   351  			// TODO-Klaytn drop or missing tx
   352  			if self.nodetype != common.CONSENSUSNODE {
   353  				if !TxResendUseLegacy {
   354  					continue
   355  				}
   356  				pending, err := self.backend.TxPool().Pending()
   357  				if err != nil {
   358  					logger.Error("Failed to fetch pending transactions", "err", err)
   359  					continue
   360  				}
   361  
   362  				if len(pending) > 0 {
   363  					accounts := len(pending)
   364  					resendTxSize := maxResendTxSize / accounts
   365  					if resendTxSize == 0 {
   366  						resendTxSize = 1
   367  					}
   368  					var resendTxs []*types.Transaction
   369  					for _, sortedTxs := range pending {
   370  						if len(sortedTxs) >= resendTxSize {
   371  							resendTxs = append(resendTxs, sortedTxs[:resendTxSize]...)
   372  						} else {
   373  							resendTxs = append(resendTxs, sortedTxs...)
   374  						}
   375  					}
   376  					if len(resendTxs) > 0 {
   377  						resentTxGauge.Update(int64(len(resendTxs)))
   378  						self.backend.ReBroadcastTxs(resendTxs)
   379  					}
   380  				}
   381  				continue
   382  			}
   383  
   384  			block := result.Block
   385  			work := result.Task
   386  
   387  			// Update the block hash in all logs since it is now available and not when the
   388  			// receipt/log of individual transactions were created.
   389  			for _, r := range work.receipts {
   390  				for _, l := range r.Logs {
   391  					l.BlockHash = block.Hash()
   392  				}
   393  			}
   394  			work.stateMu.Lock()
   395  			for _, log := range work.state.Logs() {
   396  				log.BlockHash = block.Hash()
   397  			}
   398  
   399  			start := time.Now()
   400  			result, err := self.chain.WriteBlockWithState(block, work.receipts, work.state)
   401  			work.stateMu.Unlock()
   402  			if err != nil {
   403  				if err == blockchain.ErrKnownBlock {
   404  					logger.Debug("Tried to insert already known block", "num", block.NumberU64(), "hash", block.Hash().String())
   405  				} else {
   406  					logger.Error("Failed writing block to chain", "err", err)
   407  				}
   408  				continue
   409  			}
   410  			blockWriteTime := time.Since(start)
   411  
   412  			// TODO-Klaytn-Issue264 If we are using istanbul BFT, then we always have a canonical chain.
   413  			//         Later we may be able to refine below code.
   414  
   415  			// check if canon block and write transactions
   416  			if result.Status == blockchain.CanonStatTy {
   417  				// implicit by posting ChainHeadEvent
   418  				mustCommitNewWork = false
   419  			}
   420  
   421  			// Broadcast the block and announce chain insertion event
   422  			self.mux.Post(blockchain.NewMinedBlockEvent{Block: block})
   423  
   424  			var events []interface{}
   425  
   426  			work.stateMu.RLock()
   427  			logs := work.state.Logs()
   428  			work.stateMu.RUnlock()
   429  
   430  			events = append(events, blockchain.ChainEvent{Block: block, Hash: block.Hash(), Logs: logs})
   431  			if result.Status == blockchain.CanonStatTy {
   432  				events = append(events, blockchain.ChainHeadEvent{Block: block})
   433  			}
   434  
   435  			// update governance CurrentSet if it is at an epoch block
   436  			if err := self.engine.CreateSnapshot(self.chain, block.NumberU64(), block.Hash(), nil); err != nil {
   437  				logger.Error("Failed to call snapshot", "err", err)
   438  			}
   439  
   440  			logger.Info("Successfully wrote mined block", "num", block.NumberU64(),
   441  				"hash", block.Hash(), "txs", len(block.Transactions()), "elapsed", blockWriteTime)
   442  			self.chain.PostChainEvents(events, logs)
   443  
   444  			// TODO-Klaytn-Issue264 If we are using istanbul BFT, then we always have a canonical chain.
   445  			//         Later we may be able to refine below code.
   446  			if mustCommitNewWork {
   447  				self.commitNewWork()
   448  			}
   449  		}
   450  	}
   451  }
   452  
   453  // push sends a new work task to currently live work agents.
   454  func (self *worker) push(work *Task) {
   455  	if atomic.LoadInt32(&self.mining) != 1 {
   456  		return
   457  	}
   458  	for agent := range self.agents {
   459  		atomic.AddInt32(&self.atWork, 1)
   460  		if ch := agent.Work(); ch != nil {
   461  			ch <- work
   462  		}
   463  	}
   464  }
   465  
   466  // makeCurrent creates a new environment for the current cycle.
   467  func (self *worker) makeCurrent(parent *types.Block, header *types.Header) error {
   468  	stateDB, err := self.chain.StateAt(parent.Root())
   469  	if err != nil {
   470  		return err
   471  	}
   472  	work := NewTask(self.config, types.MakeSigner(self.config, header.Number), stateDB, header)
   473  	if self.nodetype != common.CONSENSUSNODE {
   474  		work.Block = parent
   475  	}
   476  
   477  	// Keep track of transactions which return errors so they can be removed
   478  	work.tcount = 0
   479  	self.current = work
   480  	return nil
   481  }
   482  
   483  func (self *worker) commitNewWork() {
   484  	var pending map[common.Address]types.Transactions
   485  	var err error
   486  	if self.nodetype == common.CONSENSUSNODE {
   487  		// Check any fork transitions needed
   488  		pending, err = self.backend.TxPool().Pending()
   489  		if err != nil {
   490  			logger.Error("Failed to fetch pending transactions", "err", err)
   491  			return
   492  		}
   493  	}
   494  
   495  	self.mu.Lock()
   496  	defer self.mu.Unlock()
   497  	self.currentMu.Lock()
   498  	defer self.currentMu.Unlock()
   499  
   500  	parent := self.chain.CurrentBlock()
   501  	nextBlockNum := new(big.Int).Add(parent.Number(), common.Big1)
   502  	var nextBaseFee *big.Int
   503  	if self.nodetype == common.CONSENSUSNODE {
   504  		if self.config.IsMagmaForkEnabled(nextBlockNum) {
   505  			// NOTE-klaytn NextBlockBaseFee needs the header of parent, self.chain.CurrentBlock
   506  			// So above code, TxPool().Pending(), is separated with this and can be refactored later.
   507  			nextBaseFee = misc.NextMagmaBlockBaseFee(parent.Header(), self.config.Governance.KIP71)
   508  			pending = types.FilterTransactionWithBaseFee(pending, nextBaseFee)
   509  		}
   510  	}
   511  
   512  	// TODO-Klaytn drop or missing tx
   513  	tstart := time.Now()
   514  	tstamp := tstart.Unix()
   515  	if self.nodetype == common.CONSENSUSNODE {
   516  		ideal := time.Unix(parent.Time().Int64()+params.BlockGenerationInterval, 0)
   517  		// If a timestamp of this block is faster than the ideal timestamp,
   518  		// wait for a while and get a new timestamp
   519  		if tstart.Before(ideal) {
   520  			wait := ideal.Sub(tstart)
   521  			logger.Info("Mining too far in the future", "wait", common.PrettyDuration(wait))
   522  			time.Sleep(wait)
   523  
   524  			tstart = time.Now()    // refresh for metrics
   525  			tstamp = tstart.Unix() // refresh for block timestamp
   526  		}
   527  	}
   528  
   529  	header := &types.Header{
   530  		ParentHash: parent.Hash(),
   531  		Number:     nextBlockNum,
   532  		Extra:      self.extra,
   533  		Time:       big.NewInt(tstamp),
   534  	}
   535  	if self.config.IsMagmaForkEnabled(nextBlockNum) {
   536  		header.BaseFee = nextBaseFee
   537  	}
   538  	if err := self.engine.Prepare(self.chain, header); err != nil {
   539  		logger.Error("Failed to prepare header for mining", "err", err)
   540  		return
   541  	}
   542  	// Could potentially happen if starting to mine in an odd state.
   543  	err = self.makeCurrent(parent, header)
   544  	if err != nil {
   545  		logger.Error("Failed to create mining context", "err", err)
   546  		return
   547  	}
   548  
   549  	// Obtain current work's state lock after we receive new work assignment.
   550  	self.current.stateMu.Lock()
   551  	defer self.current.stateMu.Unlock()
   552  
   553  	// Create the current work task
   554  	work := self.current
   555  	if self.nodetype == common.CONSENSUSNODE {
   556  		txs := types.NewTransactionsByTimeAndNonce(self.current.signer, pending)
   557  		work.commitTransactions(self.mux, txs, self.chain, self.rewardbase)
   558  		finishedCommitTx := time.Now()
   559  
   560  		// Create the new block to seal with the consensus engine
   561  		if work.Block, err = self.engine.Finalize(self.chain, header, work.state, work.txs, work.receipts); err != nil {
   562  			logger.Error("Failed to finalize block for sealing", "err", err)
   563  			return
   564  		}
   565  		finishedFinalize := time.Now()
   566  
   567  		// We only care about logging if we're actually mining.
   568  		if atomic.LoadInt32(&self.mining) == 1 {
   569  			// Update the metrics subsystem with all the measurements
   570  			accountReadTimer.Update(work.state.AccountReads)
   571  			accountHashTimer.Update(work.state.AccountHashes)
   572  			accountUpdateTimer.Update(work.state.AccountUpdates)
   573  			accountCommitTimer.Update(work.state.AccountCommits)
   574  
   575  			storageReadTimer.Update(work.state.StorageReads)
   576  			storageHashTimer.Update(work.state.StorageHashes)
   577  			storageUpdateTimer.Update(work.state.StorageUpdates)
   578  			storageCommitTimer.Update(work.state.StorageCommits)
   579  
   580  			snapshotAccountReadTimer.Update(work.state.SnapshotAccountReads)
   581  			snapshotStorageReadTimer.Update(work.state.SnapshotStorageReads)
   582  			snapshotCommitTimer.Update(work.state.SnapshotCommits)
   583  
   584  			calcDeferredRewardTimer.Update(reward.CalcDeferredRewardTimer)
   585  
   586  			trieAccess := work.state.AccountReads + work.state.AccountHashes + work.state.AccountUpdates + work.state.AccountCommits
   587  			trieAccess += work.state.StorageReads + work.state.StorageHashes + work.state.StorageUpdates + work.state.StorageCommits
   588  
   589  			tCountGauge.Update(int64(work.tcount))
   590  			blockMiningTime := time.Since(tstart)
   591  			commitTxTime := finishedCommitTx.Sub(tstart)
   592  			finalizeTime := finishedFinalize.Sub(finishedCommitTx)
   593  
   594  			if header.BaseFee != nil {
   595  				blockBaseFee.Update(header.BaseFee.Int64() / int64(params.Ston))
   596  			}
   597  			blockMiningTimer.Update(blockMiningTime)
   598  			blockMiningCommitTxTimer.Update(commitTxTime)
   599  			blockMiningExecuteTxTimer.Update(commitTxTime - trieAccess)
   600  			blockMiningFinalizeTimer.Update(finalizeTime)
   601  			logger.Info("Commit new mining work",
   602  				"number", work.Block.Number(), "hash", work.Block.Hash(),
   603  				"txs", work.tcount, "elapsed", common.PrettyDuration(blockMiningTime),
   604  				"commitTime", common.PrettyDuration(commitTxTime), "finalizeTime", common.PrettyDuration(finalizeTime))
   605  		}
   606  	}
   607  
   608  	self.push(work)
   609  	self.updateSnapshot()
   610  }
   611  
   612  func (self *worker) updateSnapshot() {
   613  	self.snapshotMu.Lock()
   614  	defer self.snapshotMu.Unlock()
   615  
   616  	self.snapshotBlock = types.NewBlock(
   617  		self.current.header,
   618  		self.current.txs,
   619  		self.current.receipts,
   620  	)
   621  	self.snapshotState = self.current.state.Copy()
   622  }
   623  
   624  func (env *Task) commitTransactions(mux *event.TypeMux, txs *types.TransactionsByTimeAndNonce, bc BlockChain, rewardbase common.Address) {
   625  	coalescedLogs := env.ApplyTransactions(txs, bc, rewardbase)
   626  
   627  	if len(coalescedLogs) > 0 || env.tcount > 0 {
   628  		// make a copy, the state caches the logs and these logs get "upgraded" from pending to mined
   629  		// logs by filling in the block hash when the block was mined by the local miner. This can
   630  		// cause a race condition if a log was "upgraded" before the PendingLogsEvent is processed.
   631  		cpy := make([]*types.Log, len(coalescedLogs))
   632  		for i, l := range coalescedLogs {
   633  			cpy[i] = new(types.Log)
   634  			*cpy[i] = *l
   635  		}
   636  		go func(logs []*types.Log, tcount int) {
   637  			if len(logs) > 0 {
   638  				mux.Post(blockchain.PendingLogsEvent{Logs: logs})
   639  			}
   640  			if tcount > 0 {
   641  				mux.Post(blockchain.PendingStateEvent{})
   642  			}
   643  		}(cpy, env.tcount)
   644  	}
   645  }
   646  
   647  func (env *Task) ApplyTransactions(txs *types.TransactionsByTimeAndNonce, bc BlockChain, rewardbase common.Address) []*types.Log {
   648  	var coalescedLogs []*types.Log
   649  
   650  	// Limit the execution time of all transactions in a block
   651  	var abort int32 = 0       // To break the below commitTransaction for loop when timed out
   652  	chDone := make(chan bool) // To stop the goroutine below when processing txs is completed
   653  
   654  	// chEVM is used to notify the below goroutine of the running EVM so it can call evm.Cancel
   655  	// when timed out.  We use a buffered channel to prevent the main EVM execution routine
   656  	// from being blocked due to the channel communication.
   657  	chEVM := make(chan *vm.EVM, 1)
   658  
   659  	go func() {
   660  		blockTimer := time.NewTimer(params.BlockGenerationTimeLimit)
   661  		timeout := false
   662  		var evm *vm.EVM
   663  
   664  		for {
   665  			select {
   666  			case <-blockTimer.C:
   667  				timeout = true
   668  				atomic.StoreInt32(&abort, 1)
   669  
   670  			case <-chDone:
   671  				// Everything is done. Stop this goroutine.
   672  				return
   673  
   674  			case evm = <-chEVM:
   675  			}
   676  
   677  			if timeout && evm != nil {
   678  				// Allow the first transaction to complete although it exceeds the time limit.
   679  				if env.tcount > 0 {
   680  					// The total time limit reached, thus we stop the currently running EVM.
   681  					evm.Cancel(vm.CancelByTotalTimeLimit)
   682  				}
   683  				evm = nil
   684  			}
   685  		}
   686  	}()
   687  
   688  	vmConfig := &vm.Config{
   689  		RunningEVM:               chEVM,
   690  		UseOpcodeComputationCost: true,
   691  	}
   692  
   693  	var numTxsChecked int64 = 0
   694  	var numTxsNonceTooLow int64 = 0
   695  	var numTxsNonceTooHigh int64 = 0
   696  	var numTxsGasLimitReached int64 = 0
   697  CommitTransactionLoop:
   698  	for atomic.LoadInt32(&abort) == 0 {
   699  		// Retrieve the next transaction and abort if all done
   700  		tx := txs.Peek()
   701  		if tx == nil {
   702  			// To indicate that it does not have enough transactions for params.BlockGenerationTimeLimit.
   703  			if numTxsChecked > 0 {
   704  				usedAllTxsCounter.Inc(1)
   705  			}
   706  			break
   707  		}
   708  		numTxsChecked++
   709  		// Error may be ignored here. The error has already been checked
   710  		// during transaction acceptance is the transaction pool.
   711  		//
   712  		// We use the eip155 signer regardless of the current hf.
   713  		from, _ := types.Sender(env.signer, tx)
   714  
   715  		// NOTE-Klaytn Since Klaytn is always in EIP155, the below replay protection code is not needed.
   716  		// TODO-Klaytn-RemoveLater Remove the code commented below.
   717  		// Check whether the tx is replay protected. If we're not in the EIP155 hf
   718  		// phase, start ignoring the sender until we do.
   719  		//if tx.Protected() && !env.config.IsEIP155(env.header.Number) {
   720  		//	logger.Trace("Ignoring reply protected transaction", "hash", tx.Hash())
   721  		//	//logger.Error("#### worker.commitTransaction","tx.protected",tx.Protected(),"tx.hash",tx.Hash(),"nonce",tx.Nonce(),"to",tx.To())
   722  		//	txs.Pop()
   723  		//	continue
   724  		//}
   725  		// Start executing the transaction
   726  		env.state.Prepare(tx.Hash(), common.Hash{}, env.tcount)
   727  
   728  		err, logs := env.commitTransaction(tx, bc, rewardbase, vmConfig)
   729  		switch err {
   730  		case blockchain.ErrGasLimitReached:
   731  			// Pop the current out-of-gas transaction without shifting in the next from the account
   732  			logger.Trace("Gas limit exceeded for current block", "sender", from)
   733  			numTxsGasLimitReached++
   734  			txs.Pop()
   735  
   736  		case blockchain.ErrNonceTooLow:
   737  			// New head notification data race between the transaction pool and miner, shift
   738  			logger.Trace("Skipping transaction with low nonce", "sender", from, "nonce", tx.Nonce())
   739  			numTxsNonceTooLow++
   740  			txs.Shift()
   741  
   742  		case blockchain.ErrNonceTooHigh:
   743  			// Reorg notification data race between the transaction pool and miner, skip account =
   744  			logger.Trace("Skipping account with high nonce", "sender", from, "nonce", tx.Nonce())
   745  			numTxsNonceTooHigh++
   746  			txs.Pop()
   747  
   748  		case vm.ErrTotalTimeLimitReached:
   749  			logger.Warn("Transaction aborted due to time limit", "hash", tx.Hash().String())
   750  			timeLimitReachedCounter.Inc(1)
   751  			if env.tcount == 0 {
   752  				logger.Error("A single transaction exceeds total time limit", "hash", tx.Hash().String())
   753  				tooLongTxCounter.Inc(1)
   754  			}
   755  			// NOTE-Klaytn Exit for loop immediately without checking abort variable again.
   756  			break CommitTransactionLoop
   757  
   758  		case blockchain.ErrTxTypeNotSupported:
   759  			// Pop the unsupported transaction without shifting in the next from the account
   760  			logger.Trace("Skipping unsupported transaction type", "sender", from, "type", tx.Type())
   761  			txs.Pop()
   762  
   763  		case nil:
   764  			// Everything ok, collect the logs and shift in the next transaction from the same account
   765  			coalescedLogs = append(coalescedLogs, logs...)
   766  			env.tcount++
   767  			txs.Shift()
   768  
   769  		default:
   770  			// Strange error, discard the transaction and get the next in line (note, the
   771  			// nonce-too-high clause will prevent us from executing in vain).
   772  			logger.Warn("Transaction failed, account skipped", "sender", from, "hash", tx.Hash().String(), "err", err)
   773  			strangeErrorTxsCounter.Inc(1)
   774  			txs.Shift()
   775  		}
   776  	}
   777  
   778  	// Update the number of transactions checked and dropped during ApplyTransactions.
   779  	checkedTxsGauge.Update(numTxsChecked)
   780  	nonceTooLowTxsGauge.Update(numTxsNonceTooLow)
   781  	nonceTooHighTxsGauge.Update(numTxsNonceTooHigh)
   782  	gasLimitReachedTxsGauge.Update(numTxsGasLimitReached)
   783  
   784  	// Stop the goroutine that has been handling the timer.
   785  	chDone <- true
   786  
   787  	return coalescedLogs
   788  }
   789  
   790  func (env *Task) commitTransaction(tx *types.Transaction, bc BlockChain, rewardbase common.Address, vmConfig *vm.Config) (error, []*types.Log) {
   791  	snap := env.state.Snapshot()
   792  
   793  	receipt, _, err := bc.ApplyTransaction(env.config, &rewardbase, env.state, env.header, tx, &env.header.GasUsed, vmConfig)
   794  	if err != nil {
   795  		if err != vm.ErrInsufficientBalance && err != vm.ErrTotalTimeLimitReached {
   796  			tx.MarkUnexecutable(true)
   797  		}
   798  		env.state.RevertToSnapshot(snap)
   799  		return err, nil
   800  	}
   801  	env.txs = append(env.txs, tx)
   802  	env.receipts = append(env.receipts, receipt)
   803  
   804  	return nil, receipt.Logs
   805  }
   806  
   807  func NewTask(config *params.ChainConfig, signer types.Signer, statedb *state.StateDB, header *types.Header) *Task {
   808  	return &Task{
   809  		config:    config,
   810  		signer:    signer,
   811  		state:     statedb,
   812  		header:    header,
   813  		createdAt: time.Now(),
   814  	}
   815  }
   816  
   817  func (env *Task) Transactions() []*types.Transaction { return env.txs }
   818  func (env *Task) Receipts() []*types.Receipt         { return env.receipts }