github.com/n1ghtfa1l/go-vnt@v0.6.4-alpha.6/producer/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 producer
    18  
    19  import (
    20  	"encoding/json"
    21  	"math/big"
    22  	"sync"
    23  	"sync/atomic"
    24  	"time"
    25  
    26  	"github.com/vntchain/go-vnt/common"
    27  	"github.com/vntchain/go-vnt/consensus"
    28  	"github.com/vntchain/go-vnt/consensus/dpos"
    29  	"github.com/vntchain/go-vnt/core"
    30  	"github.com/vntchain/go-vnt/core/state"
    31  	"github.com/vntchain/go-vnt/core/types"
    32  	"github.com/vntchain/go-vnt/core/vm"
    33  	"github.com/vntchain/go-vnt/event"
    34  	"github.com/vntchain/go-vnt/log"
    35  	"github.com/vntchain/go-vnt/params"
    36  	"github.com/vntchain/go-vnt/vntdb"
    37  )
    38  
    39  const (
    40  	resultQueueSize     = 10
    41  	producingLogAtDepth = 5
    42  
    43  	// txChanSize is the size of channel listening to NewTxsEvent.
    44  	// The number is referenced from the size of tx pool.
    45  	txChanSize = 4096
    46  	// chainHeadChanSize is the size of channel listening to ChainHeadEvent.
    47  	chainHeadChanSize = 10
    48  	// chainSideChanSize is the size of channel listening to ChainSideEvent.
    49  	chainSideChanSize = 10
    50  )
    51  
    52  // Work is the workers current environment and holds
    53  // all of the current state information
    54  type Work struct {
    55  	config *params.ChainConfig
    56  	signer types.Signer
    57  
    58  	state   *state.StateDB // apply state changes here
    59  	tcount  int            // tx count in cycle
    60  	gasPool *core.GasPool  // available gas used to pack transactions
    61  
    62  	Block *types.Block // the new block
    63  
    64  	header   *types.Header
    65  	txs      []*types.Transaction
    66  	receipts []*types.Receipt
    67  
    68  	createdAt time.Time
    69  }
    70  
    71  type Result struct {
    72  	Work  *Work
    73  	Block *types.Block
    74  }
    75  
    76  // worker is the main object which takes care of applying messages to the new state
    77  type worker struct {
    78  	config *params.ChainConfig
    79  	engine consensus.Engine
    80  
    81  	mu sync.Mutex
    82  
    83  	// update loop
    84  	mux          *event.TypeMux
    85  	txsCh        chan core.NewTxsEvent
    86  	txsSub       event.Subscription
    87  	chainHeadCh  chan core.ChainHeadEvent
    88  	chainHeadSub event.Subscription
    89  	chainSideCh  chan core.ChainSideEvent
    90  	chainSideSub event.Subscription
    91  	recBftMsgSub *event.TypeMuxSubscription
    92  	wg           sync.WaitGroup
    93  
    94  	vnt     Backend
    95  	chain   *core.BlockChain
    96  	proc    core.Validator
    97  	chainDb vntdb.Database
    98  
    99  	coinbase common.Address
   100  	extra    []byte
   101  
   102  	currentMu sync.Mutex
   103  	current   *Work
   104  
   105  	snapshotMu    sync.RWMutex
   106  	snapshotBlock *types.Block
   107  	snapshotState *state.StateDB
   108  
   109  	unconfirmed *unconfirmedBlocks // set of locally produced blocks pending canonicalness confirmations
   110  
   111  	// atomic status counters
   112  	producing int32
   113  	atWork    int32
   114  
   115  	roundTimer      *time.Timer // Timer to trigger each round of producing block
   116  	resetTimerEvent chan *big.Int
   117  	producerStop    chan struct{}
   118  }
   119  
   120  func newWorker(config *params.ChainConfig, engine consensus.Engine, coinbase common.Address, vnt Backend, mux *event.TypeMux) *worker {
   121  	worker := &worker{
   122  		config:          config,
   123  		engine:          engine,
   124  		vnt:             vnt,
   125  		mux:             mux,
   126  		txsCh:           make(chan core.NewTxsEvent, txChanSize),
   127  		chainHeadCh:     make(chan core.ChainHeadEvent, chainHeadChanSize),
   128  		chainSideCh:     make(chan core.ChainSideEvent, chainSideChanSize),
   129  		chainDb:         vnt.ChainDb(),
   130  		chain:           vnt.BlockChain(),
   131  		proc:            vnt.BlockChain().Validator(),
   132  		coinbase:        coinbase,
   133  		unconfirmed:     newUnconfirmedBlocks(vnt.BlockChain(), producingLogAtDepth),
   134  		roundTimer:      time.NewTimer(time.Second),
   135  		resetTimerEvent: make(chan *big.Int, 1),
   136  		producerStop:    make(chan struct{}, 1),
   137  	}
   138  	worker.stopRoundTimer()
   139  
   140  	// Subscribe NewTxsEvent for tx pool
   141  	worker.txsSub = vnt.TxPool().SubscribeNewTxsEvent(worker.txsCh)
   142  	// Subscribe events for blockchain
   143  	worker.chainHeadSub = vnt.BlockChain().SubscribeChainHeadEvent(worker.chainHeadCh)
   144  	worker.chainSideSub = vnt.BlockChain().SubscribeChainSideEvent(worker.chainSideCh)
   145  	worker.recBftMsgSub = worker.mux.Subscribe(core.RecBftMsgEvent{})
   146  
   147  	go worker.recBftMsg()
   148  	go worker.update()
   149  
   150  	worker.commitNewWork()
   151  
   152  	return worker
   153  }
   154  
   155  func (self *worker) setCoinbase(addr common.Address) {
   156  	self.mu.Lock()
   157  	defer self.mu.Unlock()
   158  	self.coinbase = addr
   159  }
   160  
   161  func (self *worker) setExtra(extra []byte) {
   162  	self.mu.Lock()
   163  	defer self.mu.Unlock()
   164  	self.extra = extra
   165  }
   166  
   167  func (self *worker) pending() (*types.Block, *state.StateDB) {
   168  	if atomic.LoadInt32(&self.producing) == 0 {
   169  		// return a snapshot to avoid contention on currentMu mutex
   170  		self.snapshotMu.RLock()
   171  		defer self.snapshotMu.RUnlock()
   172  		return self.snapshotBlock, self.snapshotState.Copy()
   173  	}
   174  
   175  	self.currentMu.Lock()
   176  	defer self.currentMu.Unlock()
   177  	return self.current.Block, self.current.state.Copy()
   178  }
   179  
   180  func (self *worker) pendingBlock() *types.Block {
   181  	if atomic.LoadInt32(&self.producing) == 0 {
   182  		// return a snapshot to avoid contention on currentMu mutex
   183  		self.snapshotMu.RLock()
   184  		defer self.snapshotMu.RUnlock()
   185  		return self.snapshotBlock
   186  	}
   187  
   188  	self.currentMu.Lock()
   189  	defer self.currentMu.Unlock()
   190  	return self.current.Block
   191  }
   192  
   193  func (self *worker) start() {
   194  	self.mu.Lock()
   195  	defer self.mu.Unlock()
   196  
   197  	atomic.StoreInt32(&self.producing, 1)
   198  
   199  	// Init bft
   200  	if dp, ok := self.engine.(*dpos.Dpos); ok {
   201  		dp.InitBft(self.SendBftMsg, self.SendBftPeerChangeMsg, self.chain.VerifyBlockForBft, self.writeBlock)
   202  		// 刚启动节点的bft节点设置
   203  		currentRoot := self.chain.CurrentHeader().Root
   204  		witnessesUrl := self.chain.Config().Dpos.WitnessesUrl
   205  		if db, err := self.chain.StateAt(currentRoot); err != nil {
   206  			log.Error("get current db error", "err", err)
   207  		} else {
   208  			_, urls := dp.GetWitnessesFromStateDB(db)
   209  			if len(urls) > 0 {
   210  				witnessesUrl = urls
   211  			}
   212  		}
   213  		self.SendBftPeerChangeMsg(witnessesUrl)
   214  	}
   215  }
   216  
   217  func (self *worker) stop() {
   218  	self.wg.Wait()
   219  
   220  	self.producerStop <- struct{}{}
   221  
   222  	self.mu.Lock()
   223  	defer self.mu.Unlock()
   224  	atomic.StoreInt32(&self.producing, 0)
   225  	atomic.StoreInt32(&self.atWork, 0)
   226  
   227  	if dp, ok := self.engine.(*dpos.Dpos); ok {
   228  		dp.ProducingStop()
   229  	}
   230  }
   231  
   232  func (self *worker) update() {
   233  	defer self.txsSub.Unsubscribe()
   234  	defer self.chainHeadSub.Unsubscribe()
   235  	defer self.chainSideSub.Unsubscribe()
   236  	defer self.recBftMsgSub.Unsubscribe()
   237  
   238  	for {
   239  		// A real event arrived, process interesting content
   240  		select {
   241  		// Handle ChainHeadEvent
   242  		case headEvent := <-self.chainHeadCh:
   243  			log.Debug("Worker: new block write finished", "block hash", headEvent.Block.Hash().String())
   244  			if self.config.Dpos != nil {
   245  				if dp, ok := self.engine.(*dpos.Dpos); ok {
   246  					dp.CleanOldMsg(headEvent.Block.Number())
   247  				}
   248  			}
   249  
   250  			// Handle ChainSideEvent
   251  		case ev := <-self.chainSideCh:
   252  			log.Info("Block fail to on chain", "block hash", ev.Block.Hash())
   253  
   254  			// Handle NewTxsEvent
   255  		case ev := <-self.txsCh:
   256  			// Apply transactions to the pending state if we're not producing block.
   257  			//
   258  			// Note all transactions received may not be continuous with transactions
   259  			// already included in the current producing block. These transactions will
   260  			// be automatically eliminated.
   261  			if self.config.Dpos == nil && atomic.LoadInt32(&self.producing) == 0 {
   262  				self.currentMu.Lock()
   263  				txs := make(map[common.Address]types.Transactions)
   264  				for _, tx := range ev.Txs {
   265  					acc, _ := types.Sender(self.current.signer, tx)
   266  					txs[acc] = append(txs[acc], tx)
   267  				}
   268  				txset := types.NewTransactionsByPriceAndNonce(self.current.signer, txs)
   269  				self.current.commitTransactions(self.mux, txset, self.chain, self.coinbase)
   270  				self.updateSnapshot()
   271  				self.currentMu.Unlock()
   272  			}
   273  
   274  			// Only Dpos using
   275  		case <-self.roundTimer.C:
   276  			if self.config.Dpos != nil {
   277  				self.commitNewWork()
   278  			}
   279  
   280  		case nextRoundTime := <-self.resetTimerEvent:
   281  			log.Trace("Receive reset round timer")
   282  			self.resetRoundTimer(nextRoundTime)
   283  
   284  		case <-self.producerStop:
   285  			self.stopRoundTimer()
   286  
   287  			// System stopped
   288  		case <-self.txsSub.Err():
   289  			self.stopRoundTimer()
   290  			return
   291  		case <-self.chainHeadSub.Err():
   292  			self.stopRoundTimer()
   293  			return
   294  		case <-self.chainSideSub.Err():
   295  			self.stopRoundTimer()
   296  			return
   297  		}
   298  	}
   299  }
   300  
   301  func (self *worker) recBftMsg() {
   302  	for obj := range self.recBftMsgSub.Chan() {
   303  		switch ev := obj.Data.(type) {
   304  		case core.RecBftMsgEvent:
   305  			self.engine.HandleBftMsg(self.chain, ev.BftMsg.Msg)
   306  		default:
   307  			log.Warn("Receive bft msg, but type unknown")
   308  		}
   309  	}
   310  }
   311  
   312  // push sends a new work task to currently live producer agents.
   313  func (self *worker) push(work *Work) {
   314  	if atomic.LoadInt32(&self.producing) != 1 {
   315  		return
   316  	}
   317  
   318  	if _, err := self.engine.Seal(self.chain, work.Block, nil); err != nil {
   319  		log.Warn("Block sealing failed", "err", err)
   320  	}
   321  }
   322  
   323  func (self *worker) SendBftMsg(msg types.ConsensusMsg) {
   324  	self.mux.Post(core.SendBftMsgEvent{
   325  		BftMsg: types.BftMsg{
   326  			BftType: msg.Type(),
   327  			Msg:     msg,
   328  		}})
   329  }
   330  
   331  func (self *worker) SendBftPeerChangeMsg(urls []string) {
   332  	self.mux.Post(core.BftPeerChangeEvent{
   333  		Urls: urls,
   334  	})
   335  }
   336  
   337  // writeBlock write block to block chain, and post NewProducedBlockEvent
   338  func (self *worker) writeBlock(block *types.Block) error {
   339  	if err := self.chain.WriteBlock(block); err != nil {
   340  		log.Error("Failed writing block to chain", "err", err)
   341  		return err
   342  	}
   343  
   344  	// Broadcast the block and announce chain insertion event
   345  	self.mux.Post(core.NewProducedBlockEvent{Block: block})
   346  	return nil
   347  }
   348  
   349  // makeCurrent creates a new environment for the current cycle.
   350  func (self *worker) makeCurrent(parent *types.Block, header *types.Header) error {
   351  	log.Trace("Enter make current")
   352  
   353  	state, err := self.chain.StateAt(parent.Root())
   354  	if err != nil {
   355  		return err
   356  	}
   357  	work := &Work{
   358  		config:    self.config,
   359  		signer:    types.NewHubbleSigner(self.config.ChainID),
   360  		state:     state,
   361  		header:    header,
   362  		createdAt: time.Now(),
   363  	}
   364  
   365  	// Keep track of transactions which return errors so they can be removed
   366  	work.tcount = 0
   367  	self.current = work
   368  
   369  	return nil
   370  }
   371  
   372  func (self *worker) commitNewWork() {
   373  	log.Trace("commitNewWork start")
   374  
   375  	self.mu.Lock()
   376  	defer self.mu.Unlock()
   377  	self.currentMu.Lock()
   378  	defer self.currentMu.Unlock()
   379  
   380  	tstart := time.Now()
   381  	tstamp := tstart.Unix()
   382  	parent := self.chain.CurrentBlock()
   383  
   384  	// Do not work too try before parent block
   385  	wait := time.Unix(parent.Time().Int64(), 0).Sub(tstart)
   386  	if wait > 0 {
   387  		log.Info("CommitNewWork start before parent's block time, wait", "wait", wait)
   388  		time.Sleep(wait)
   389  	}
   390  
   391  	num := parent.Number()
   392  	header := &types.Header{
   393  		ParentHash: parent.Hash(),
   394  		Number:     num.Add(num, common.Big1),
   395  		GasLimit:   core.CalcGasLimit(parent),
   396  		Extra:      self.extra,
   397  		Time:       big.NewInt(tstamp),
   398  	}
   399  	// Only set the coinbase if we are producing (avoid spurious block rewards)
   400  	if atomic.LoadInt32(&self.producing) == 1 {
   401  		header.Coinbase = self.coinbase
   402  	}
   403  
   404  	preErr := self.engine.Prepare(self.chain, header)
   405  	if atomic.LoadInt32(&self.producing) == 1 {
   406  		log.Trace("Send reset round timer")
   407  		self.resetTimerEvent <- header.Time
   408  	}
   409  	if time.Unix(header.Time.Int64(), 0).Sub(time.Now()) <= 0 {
   410  		log.Warn("Prepare use too much time, missing out your turn")
   411  		return
   412  	}
   413  
   414  	// Could potentially happen if starting to produce in an odd state.
   415  	err := self.makeCurrent(parent, header)
   416  	if err != nil {
   417  		log.Error("Failed to create producing context", "err", err)
   418  		return
   419  	}
   420  	// Create the current work task and check any fork transitions needed
   421  	work := self.current
   422  	pending, err := self.vnt.TxPool().Pending()
   423  	if err != nil {
   424  		log.Error("Failed to fetch pending transactions", "err", err)
   425  		return
   426  	}
   427  	txs := types.NewTransactionsByPriceAndNonce(self.current.signer, pending)
   428  	work.commitTransactions(self.mux, txs, self.chain, self.coinbase)
   429  
   430  	// Create the new block to seal with the consensus engine
   431  	if work.Block, err = self.engine.Finalize(self.chain, header, work.state, work.txs, work.receipts); err != nil {
   432  		log.Error("Failed to finalize block for sealing", "err", err)
   433  		return
   434  	}
   435  	blockheaderjson, _ := json.Marshal(work.Block.Header())
   436  	blocktxjson, _ := json.Marshal(work.Block.Transactions())
   437  	log.Debug("worker", "func", "commitNewWork", "block header", string(blockheaderjson), "block tx", string(blocktxjson))
   438  	// We only care about logging if we're actually producing.
   439  	if atomic.LoadInt32(&self.producing) == 1 {
   440  		log.Info("Commit new producing work", "number", work.Block.Number(), "txs", work.tcount, "elapsed", common.PrettyDuration(time.Since(tstart)))
   441  		self.unconfirmed.Shift(work.Block.NumberU64() - 1)
   442  	}
   443  
   444  	// This is time consuming. The max time has been used is 4.3ms with 220txs in a block.
   445  	self.updateSnapshot()
   446  
   447  	if preErr != nil {
   448  		log.Debug("Failed to prepare header for producing", "preErr", preErr)
   449  		return
   450  	}
   451  
   452  	// updateSnapshot() is time consuming. If push() before updateSnapshot(), Gvnt may be
   453  	// stop for concurrent map iteration and map write. After push(), a block generated
   454  	// will be write to statedb, and may be updateSnapshot() still read statedb. Then
   455  	// an error occurs.
   456  	self.push(work)
   457  }
   458  
   459  // Reset the clock for the next period
   460  func (self *worker) resetRoundTimer(nextRoundTime *big.Int) {
   461  	dur := time.Unix(nextRoundTime.Int64(), 0).Sub(time.Now())
   462  	// Always make sure timer stoped and cleaned before Reset()
   463  	self.stopRoundTimer()
   464  	self.roundTimer.Reset(dur)
   465  	log.Debug("Reset round timer", "header.time", nextRoundTime, "time", time.Now().Unix(), "dur", dur)
   466  }
   467  
   468  func (self *worker) stopRoundTimer() {
   469  	// The stop command may be happen when the round timer is timeout but not deal with
   470  	// the timeout event, so cleaning the channel of roundTimer is needed.
   471  	if false == self.roundTimer.Stop() && len(self.roundTimer.C) > 0 {
   472  		<-self.roundTimer.C
   473  		log.Warn("worker.roundTimer.C still has a expired event, now has been cleaned")
   474  	}
   475  }
   476  
   477  func (self *worker) updateSnapshot() {
   478  	self.snapshotMu.Lock()
   479  	defer self.snapshotMu.Unlock()
   480  
   481  	self.snapshotBlock = types.NewBlock(
   482  		self.current.header,
   483  		self.current.txs,
   484  		self.current.receipts,
   485  	)
   486  	self.snapshotState = self.current.state.Copy()
   487  }
   488  
   489  func (env *Work) commitTransactions(mux *event.TypeMux, txs *types.TransactionsByPriceAndNonce, bc *core.BlockChain, coinbase common.Address) {
   490  	if env.gasPool == nil {
   491  		env.gasPool = new(core.GasPool).AddGas(env.header.GasLimit)
   492  	}
   493  
   494  	var coalescedLogs []*types.Log
   495  
   496  	for {
   497  		// If we don't have enough gas for any further transactions then we're done
   498  		if env.gasPool.Gas() < params.TxGas {
   499  			log.Trace("Not enough gas for further transactions", "have", env.gasPool, "want", params.TxGas)
   500  			break
   501  		}
   502  		// Retrieve the next transaction and abort if all done
   503  		tx := txs.Peek()
   504  		if tx == nil {
   505  			break
   506  		}
   507  		// Error may be ignored here. The error has already been checked
   508  		// during transaction acceptance is the transaction pool.
   509  		//
   510  		// We use the eip155 signer regardless of the current hf.
   511  		from, _ := types.Sender(env.signer, tx)
   512  
   513  		// Start executing the transaction
   514  		env.state.Prepare(tx.Hash(), common.Hash{}, env.tcount)
   515  
   516  		err, logs := env.commitTransaction(tx, bc, coinbase, env.gasPool)
   517  		switch err {
   518  		case core.ErrGasLimitReached:
   519  			// Pop the current out-of-gas transaction without shifting in the next from the account
   520  			log.Trace("Gas limit exceeded for current block", "sender", from)
   521  			txs.Pop()
   522  
   523  		case core.ErrNonceTooLow:
   524  			// New head notification data race between the transaction pool and producer, shift
   525  			log.Trace("Skipping transaction with low nonce", "sender", from, "nonce", tx.Nonce())
   526  			txs.Shift()
   527  
   528  		case core.ErrNonceTooHigh:
   529  			// Reorg notification data race between the transaction pool and producer, skip account =
   530  			log.Trace("Skipping account with hight nonce", "sender", from, "nonce", tx.Nonce())
   531  			txs.Pop()
   532  
   533  		case nil:
   534  			// Everything ok, collect the logs and shift in the next transaction from the same account
   535  			coalescedLogs = append(coalescedLogs, logs...)
   536  			env.tcount++
   537  			txs.Shift()
   538  
   539  		default:
   540  			// Strange error, discard the transaction and get the next in line (note, the
   541  			// nonce-too-high clause will prevent us from executing in vain).
   542  			log.Debug("Transaction failed, account skipped", "hash", tx.Hash(), "err", err)
   543  			txs.Shift()
   544  		}
   545  	}
   546  
   547  	if len(coalescedLogs) > 0 || env.tcount > 0 {
   548  		// make a copy, the state caches the logs and these logs get "upgraded" from pending to produced
   549  		// logs by filling in the block hash when the block was produced by the local producer. This can
   550  		// cause a race condition if a log was "upgraded" before the PendingLogsEvent is processed.
   551  		cpy := make([]*types.Log, len(coalescedLogs))
   552  		for i, l := range coalescedLogs {
   553  			cpy[i] = new(types.Log)
   554  			*cpy[i] = *l
   555  		}
   556  		go func(logs []*types.Log, tcount int) {
   557  			if len(logs) > 0 {
   558  				mux.Post(core.PendingLogsEvent{Logs: logs})
   559  			}
   560  			if tcount > 0 {
   561  				mux.Post(core.PendingStateEvent{})
   562  			}
   563  		}(cpy, env.tcount)
   564  	}
   565  }
   566  
   567  func (env *Work) commitTransaction(tx *types.Transaction, bc *core.BlockChain, coinbase common.Address, gp *core.GasPool) (error, []*types.Log) {
   568  	snap := env.state.Snapshot()
   569  
   570  	receipt, _, err := core.ApplyTransaction(env.config, bc, &coinbase, gp, env.state, env.header, tx, &env.header.GasUsed, vm.Config{})
   571  	if err != nil {
   572  		env.state.RevertToSnapshot(snap)
   573  		return err, nil
   574  	}
   575  	env.txs = append(env.txs, tx)
   576  	env.receipts = append(env.receipts, receipt)
   577  
   578  	return nil, receipt.Logs
   579  }