github.com/neatio-net/neatio@v1.7.3-0.20231114194659-f4d7a2226baa/utilities/miner/worker.go (about)

     1  package miner
     2  
     3  import (
     4  	"fmt"
     5  	"math/big"
     6  	"sync"
     7  	"sync/atomic"
     8  	"time"
     9  
    10  	"github.com/neatio-net/neatio/chain/consensus"
    11  	ntcTypes "github.com/neatio-net/neatio/chain/consensus/neatcon/types"
    12  	"github.com/neatio-net/neatio/chain/core"
    13  	"github.com/neatio-net/neatio/chain/core/state"
    14  	"github.com/neatio-net/neatio/chain/core/types"
    15  	"github.com/neatio-net/neatio/chain/core/vm"
    16  	"github.com/neatio-net/neatio/chain/log"
    17  	"github.com/neatio-net/neatio/params"
    18  	"github.com/neatio-net/neatio/utilities/common"
    19  	"github.com/neatio-net/neatio/utilities/event"
    20  	"github.com/neatio-net/set-go"
    21  )
    22  
    23  const (
    24  	resultQueueSize  = 10
    25  	miningLogAtDepth = 5
    26  
    27  	txChanSize = 4096
    28  
    29  	chainHeadChanSize = 10
    30  
    31  	chainSideChanSize = 10
    32  )
    33  
    34  type Agent interface {
    35  	Work() chan<- *Work
    36  	SetReturnCh(chan<- *Result)
    37  	Stop()
    38  	Start()
    39  }
    40  
    41  type Work struct {
    42  	signer types.Signer
    43  
    44  	state     *state.StateDB
    45  	ancestors *set.Set
    46  	family    *set.Set
    47  	uncles    *set.Set
    48  	tcount    int
    49  
    50  	Block *types.Block
    51  
    52  	header   *types.Header
    53  	txs      []*types.Transaction
    54  	receipts []*types.Receipt
    55  
    56  	ops *types.PendingOps
    57  
    58  	createdAt time.Time
    59  	logger    log.Logger
    60  }
    61  
    62  type Result struct {
    63  	Work         *Work
    64  	Block        *types.Block
    65  	Intermediate *ntcTypes.IntermediateBlockResult
    66  }
    67  
    68  type worker struct {
    69  	config *params.ChainConfig
    70  	engine consensus.Engine
    71  	eth    Backend
    72  	chain  *core.BlockChain
    73  
    74  	gasFloor uint64
    75  	gasCeil  uint64
    76  
    77  	mu sync.Mutex
    78  
    79  	mux          *event.TypeMux
    80  	txCh         chan core.TxPreEvent
    81  	txSub        event.Subscription
    82  	chainHeadCh  chan core.ChainHeadEvent
    83  	chainHeadSub event.Subscription
    84  	chainSideCh  chan core.ChainSideEvent
    85  	chainSideSub event.Subscription
    86  	wg           sync.WaitGroup
    87  
    88  	agents   map[Agent]struct{}
    89  	resultCh chan *Result
    90  	exitCh   chan struct{}
    91  
    92  	proc core.Validator
    93  
    94  	coinbase common.Address
    95  	extra    []byte
    96  
    97  	currentMu sync.Mutex
    98  	current   *Work
    99  
   100  	uncleMu        sync.Mutex
   101  	possibleUncles map[common.Hash]*types.Block
   102  
   103  	unconfirmed *unconfirmedBlocks
   104  
   105  	mining int32
   106  	atWork int32
   107  
   108  	logger log.Logger
   109  	cch    core.CrossChainHelper
   110  }
   111  
   112  func newWorker(config *params.ChainConfig, engine consensus.Engine, eth Backend, mux *event.TypeMux, gasFloor, gasCeil uint64, cch core.CrossChainHelper) *worker {
   113  	worker := &worker{
   114  		config:         config,
   115  		engine:         engine,
   116  		eth:            eth,
   117  		mux:            mux,
   118  		gasFloor:       gasFloor,
   119  		gasCeil:        gasCeil,
   120  		txCh:           make(chan core.TxPreEvent, txChanSize),
   121  		chainHeadCh:    make(chan core.ChainHeadEvent, chainHeadChanSize),
   122  		chainSideCh:    make(chan core.ChainSideEvent, chainSideChanSize),
   123  		resultCh:       make(chan *Result, resultQueueSize),
   124  		exitCh:         make(chan struct{}),
   125  		chain:          eth.BlockChain(),
   126  		proc:           eth.BlockChain().Validator(),
   127  		possibleUncles: make(map[common.Hash]*types.Block),
   128  		agents:         make(map[Agent]struct{}),
   129  		unconfirmed:    newUnconfirmedBlocks(eth.BlockChain(), miningLogAtDepth, config.ChainLogger),
   130  		logger:         config.ChainLogger,
   131  		cch:            cch,
   132  	}
   133  
   134  	worker.txSub = eth.TxPool().SubscribeTxPreEvent(worker.txCh)
   135  
   136  	worker.chainHeadSub = eth.BlockChain().SubscribeChainHeadEvent(worker.chainHeadCh)
   137  	worker.chainSideSub = eth.BlockChain().SubscribeChainSideEvent(worker.chainSideCh)
   138  
   139  	go worker.mainLoop()
   140  
   141  	go worker.resultLoop()
   142  
   143  	return worker
   144  }
   145  
   146  func (self *worker) setCoinbase(addr common.Address) {
   147  	self.mu.Lock()
   148  	defer self.mu.Unlock()
   149  	self.coinbase = addr
   150  }
   151  
   152  func (self *worker) setExtra(extra []byte) {
   153  	self.mu.Lock()
   154  	defer self.mu.Unlock()
   155  	self.extra = extra
   156  }
   157  
   158  func (self *worker) pending() (*types.Block, *state.StateDB) {
   159  	self.currentMu.Lock()
   160  	defer self.currentMu.Unlock()
   161  
   162  	if atomic.LoadInt32(&self.mining) == 0 {
   163  		return types.NewBlock(
   164  			self.current.header,
   165  			self.current.txs,
   166  			nil,
   167  			self.current.receipts,
   168  		), self.current.state.Copy()
   169  	}
   170  	return self.current.Block, self.current.state.Copy()
   171  }
   172  
   173  func (self *worker) pendingBlock() *types.Block {
   174  	self.currentMu.Lock()
   175  	defer self.currentMu.Unlock()
   176  
   177  	if atomic.LoadInt32(&self.mining) == 0 {
   178  		return types.NewBlock(
   179  			self.current.header,
   180  			self.current.txs,
   181  			nil,
   182  			self.current.receipts,
   183  		)
   184  	}
   185  	return self.current.Block
   186  }
   187  
   188  func (self *worker) start() {
   189  	self.mu.Lock()
   190  	defer self.mu.Unlock()
   191  
   192  	atomic.StoreInt32(&self.mining, 1)
   193  
   194  	if neatcon, ok := self.engine.(consensus.NeatCon); ok {
   195  		err := neatcon.Start(self.chain, self.chain.CurrentBlock, self.chain.HasBadBlock)
   196  		if err != nil {
   197  			self.logger.Error("Starting NeatCon failed", "err", err)
   198  		}
   199  	}
   200  
   201  	for agent := range self.agents {
   202  		agent.Start()
   203  	}
   204  }
   205  
   206  func (self *worker) stop() {
   207  	self.wg.Wait()
   208  
   209  	self.mu.Lock()
   210  	defer self.mu.Unlock()
   211  	if atomic.LoadInt32(&self.mining) == 1 {
   212  		for agent := range self.agents {
   213  			agent.Stop()
   214  		}
   215  	}
   216  
   217  	if stoppableEngine, ok := self.engine.(consensus.EngineStartStop); ok {
   218  		engineStopErr := stoppableEngine.Stop()
   219  		if engineStopErr != nil {
   220  			self.logger.Error("Stop Engine failed.", "err", engineStopErr)
   221  		} else {
   222  			self.logger.Info("Stop Engine Success.")
   223  		}
   224  	}
   225  
   226  	atomic.StoreInt32(&self.mining, 0)
   227  	atomic.StoreInt32(&self.atWork, 0)
   228  }
   229  
   230  func (self *worker) isRunning() bool {
   231  	return atomic.LoadInt32(&self.mining) == 1
   232  }
   233  
   234  func (self *worker) close() {
   235  	close(self.exitCh)
   236  }
   237  
   238  func (self *worker) register(agent Agent) {
   239  	self.mu.Lock()
   240  	defer self.mu.Unlock()
   241  	self.agents[agent] = struct{}{}
   242  	agent.SetReturnCh(self.resultCh)
   243  }
   244  
   245  func (self *worker) unregister(agent Agent) {
   246  	self.mu.Lock()
   247  	defer self.mu.Unlock()
   248  	delete(self.agents, agent)
   249  	agent.Stop()
   250  }
   251  
   252  func (self *worker) mainLoop() {
   253  	defer self.txSub.Unsubscribe()
   254  	defer self.chainHeadSub.Unsubscribe()
   255  	defer self.chainSideSub.Unsubscribe()
   256  
   257  	for {
   258  
   259  		select {
   260  
   261  		case ev := <-self.chainHeadCh:
   262  			if h, ok := self.engine.(consensus.Handler); ok {
   263  				h.NewChainHead(ev.Block)
   264  			}
   265  			self.commitNewWork()
   266  
   267  		case ev := <-self.chainSideCh:
   268  			self.uncleMu.Lock()
   269  			self.possibleUncles[ev.Block.Hash()] = ev.Block
   270  			self.uncleMu.Unlock()
   271  
   272  		case ev := <-self.txCh:
   273  
   274  			if !self.isRunning() && self.current != nil {
   275  				self.currentMu.Lock()
   276  				acc, _ := types.Sender(self.current.signer, ev.Tx)
   277  				txs := map[common.Address]types.Transactions{acc: {ev.Tx}}
   278  				txset := types.NewTransactionsByPriceAndNonce(self.current.signer, txs)
   279  
   280  				self.commitTransactionsEx(txset, self.coinbase, big.NewInt(0), self.cch)
   281  				self.currentMu.Unlock()
   282  			}
   283  
   284  		case <-self.exitCh:
   285  			return
   286  		case <-self.txSub.Err():
   287  			return
   288  		case <-self.chainHeadSub.Err():
   289  			return
   290  		case <-self.chainSideSub.Err():
   291  			return
   292  		}
   293  	}
   294  }
   295  
   296  func (self *worker) resultLoop() {
   297  	for {
   298  		mustCommitNewWork := true
   299  		select {
   300  		case result := <-self.resultCh:
   301  			atomic.AddInt32(&self.atWork, -1)
   302  
   303  			if result == nil {
   304  				continue
   305  			}
   306  
   307  			var block *types.Block
   308  			var receipts types.Receipts
   309  			var state *state.StateDB
   310  			var ops *types.PendingOps
   311  
   312  			if result.Work != nil {
   313  				block = result.Block
   314  				hash := block.Hash()
   315  				work := result.Work
   316  
   317  				for i, receipt := range work.receipts {
   318  
   319  					receipt.BlockHash = hash
   320  					receipt.BlockNumber = block.Number()
   321  					receipt.TransactionIndex = uint(i)
   322  
   323  					for _, l := range receipt.Logs {
   324  						l.BlockHash = hash
   325  					}
   326  				}
   327  				for _, log := range work.state.Logs() {
   328  					log.BlockHash = hash
   329  				}
   330  				receipts = work.receipts
   331  				state = work.state
   332  				ops = work.ops
   333  			} else if result.Intermediate != nil {
   334  				block = result.Intermediate.Block
   335  
   336  				for i, receipt := range result.Intermediate.Receipts {
   337  
   338  					receipt.BlockHash = block.Hash()
   339  					receipt.BlockNumber = block.Number()
   340  					receipt.TransactionIndex = uint(i)
   341  				}
   342  
   343  				receipts = result.Intermediate.Receipts
   344  				state = result.Intermediate.State
   345  				ops = result.Intermediate.Ops
   346  			} else {
   347  				continue
   348  			}
   349  
   350  			self.chain.MuLock()
   351  
   352  			stat, err := self.chain.WriteBlockWithState(block, receipts, state)
   353  			if err != nil {
   354  				self.logger.Error("Failed writing block to chain", "err", err)
   355  				self.chain.MuUnLock()
   356  				continue
   357  			}
   358  
   359  			for _, op := range ops.Ops() {
   360  				if err := core.ApplyOp(op, self.chain, self.cch); err != nil {
   361  					log.Error("Failed executing op", op, "err", err)
   362  				}
   363  			}
   364  
   365  			if stat == core.CanonStatTy {
   366  
   367  				mustCommitNewWork = false
   368  			}
   369  
   370  			self.mux.Post(core.NewMinedBlockEvent{Block: block})
   371  			var (
   372  				events []interface{}
   373  				logs   = state.Logs()
   374  			)
   375  			events = append(events, core.ChainEvent{Block: block, Hash: block.Hash(), Logs: logs})
   376  			if stat == core.CanonStatTy {
   377  				events = append(events, core.ChainHeadEvent{Block: block})
   378  			}
   379  
   380  			self.chain.MuUnLock()
   381  
   382  			self.chain.PostChainEvents(events, logs)
   383  
   384  			self.unconfirmed.Insert(block.NumberU64(), block.Hash())
   385  
   386  			if mustCommitNewWork {
   387  				self.commitNewWork()
   388  			}
   389  		case <-self.exitCh:
   390  			return
   391  		}
   392  	}
   393  }
   394  
   395  func (self *worker) push(work *Work) {
   396  	if atomic.LoadInt32(&self.mining) != 1 {
   397  		return
   398  	}
   399  	for agent := range self.agents {
   400  		atomic.AddInt32(&self.atWork, 1)
   401  		if ch := agent.Work(); ch != nil {
   402  			ch <- work
   403  		}
   404  	}
   405  }
   406  
   407  func (self *worker) makeCurrent(parent *types.Block, header *types.Header) error {
   408  	state, err := self.chain.StateAt(parent.Root())
   409  	if err != nil {
   410  		return err
   411  	}
   412  	work := &Work{
   413  		signer:    types.NewEIP155Signer(self.config.ChainId),
   414  		state:     state,
   415  		ancestors: set.New(),
   416  		family:    set.New(),
   417  		uncles:    set.New(),
   418  		header:    header,
   419  		ops:       new(types.PendingOps),
   420  		createdAt: time.Now(),
   421  		logger:    self.logger,
   422  	}
   423  
   424  	for _, ancestor := range self.chain.GetBlocksFromHash(parent.Hash(), 7) {
   425  		for _, uncle := range ancestor.Uncles() {
   426  			work.family.Add(uncle.Hash())
   427  		}
   428  		work.family.Add(ancestor.Hash())
   429  		work.ancestors.Add(ancestor.Hash())
   430  	}
   431  
   432  	work.tcount = 0
   433  	self.current = work
   434  	return nil
   435  }
   436  
   437  func (self *worker) commitNewWork() {
   438  	self.mu.Lock()
   439  	defer self.mu.Unlock()
   440  	self.uncleMu.Lock()
   441  	defer self.uncleMu.Unlock()
   442  	self.currentMu.Lock()
   443  	defer self.currentMu.Unlock()
   444  
   445  	tstart := time.Now()
   446  	parent := self.chain.CurrentBlock()
   447  
   448  	tstamp := tstart.Unix()
   449  	if parent.Time() >= uint64(tstamp) {
   450  		tstamp = int64(parent.Time() + 1)
   451  	}
   452  
   453  	if now := time.Now().Unix(); tstamp > now+1 {
   454  		wait := time.Duration(tstamp-now) * time.Second
   455  		self.logger.Info("Mining too far in the future", "suppose but not wait", common.PrettyDuration(wait))
   456  
   457  	}
   458  
   459  	num := parent.Number()
   460  	header := &types.Header{
   461  		ParentHash: parent.Hash(),
   462  		Number:     num.Add(num, common.Big1),
   463  		GasLimit:   core.CalcGasLimit(parent, self.gasFloor, self.gasCeil),
   464  		Extra:      self.extra,
   465  		Time:       big.NewInt(tstamp),
   466  	}
   467  
   468  	if self.isRunning() {
   469  		if self.coinbase == (common.Address{}) {
   470  			log.Error("Refusing to mine without coinbase")
   471  			return
   472  		}
   473  		header.Coinbase = self.coinbase
   474  	}
   475  	if err := self.engine.Prepare(self.chain, header); err != nil {
   476  		self.logger.Error("Failed to prepare header for mining", "err", err)
   477  		return
   478  	}
   479  
   480  	err := self.makeCurrent(parent, header)
   481  	if err != nil {
   482  		self.logger.Error("Failed to create mining context", "err", err)
   483  		return
   484  	}
   485  
   486  	work := self.current
   487  
   488  	pending, err := self.eth.TxPool().Pending()
   489  	if err != nil {
   490  		self.logger.Error("Failed to fetch pending transactions", "err", err)
   491  		return
   492  	}
   493  
   494  	totalUsedMoney := big.NewInt(0)
   495  	txs := types.NewTransactionsByPriceAndNonce(self.current.signer, pending)
   496  
   497  	rmTxs := self.commitTransactionsEx(txs, self.coinbase, totalUsedMoney, self.cch)
   498  
   499  	if len(rmTxs) > 0 {
   500  		self.eth.TxPool().RemoveTxs(rmTxs)
   501  	}
   502  
   503  	var (
   504  		uncles    []*types.Header
   505  		badUncles []common.Hash
   506  	)
   507  	for hash, uncle := range self.possibleUncles {
   508  		if len(uncles) == 2 {
   509  			break
   510  		}
   511  		if err := self.commitUncle(work, uncle.Header()); err != nil {
   512  			self.logger.Trace("Bad uncle found and will be removed", "hash", hash)
   513  			self.logger.Trace(fmt.Sprint(uncle))
   514  
   515  			badUncles = append(badUncles, hash)
   516  		} else {
   517  			self.logger.Debug("Committing new uncle to block", "hash", hash)
   518  			uncles = append(uncles, uncle.Header())
   519  		}
   520  	}
   521  	for _, hash := range badUncles {
   522  		delete(self.possibleUncles, hash)
   523  	}
   524  
   525  	if work.Block, err = self.engine.Finalize(self.chain, header, work.state, work.txs, totalUsedMoney, uncles, work.receipts, work.ops); err != nil {
   526  		self.logger.Error("Failed to finalize block for sealing", "err", err)
   527  		return
   528  	}
   529  
   530  	if self.isRunning() {
   531  
   532  		self.unconfirmed.Shift(work.Block.NumberU64() - 1)
   533  	}
   534  	self.push(work)
   535  }
   536  
   537  func (self *worker) commitUncle(work *Work, uncle *types.Header) error {
   538  	hash := uncle.Hash()
   539  	if work.uncles.Has(hash) {
   540  		return fmt.Errorf("uncle not unique")
   541  	}
   542  	if !work.ancestors.Has(uncle.ParentHash) {
   543  		return fmt.Errorf("uncle's parent unknown (%x)", uncle.ParentHash[0:4])
   544  	}
   545  	if work.family.Has(hash) {
   546  		return fmt.Errorf("uncle already in family (%x)", hash)
   547  	}
   548  	work.uncles.Add(uncle.Hash())
   549  	return nil
   550  }
   551  
   552  func (self *worker) commitTransactionsEx(txs *types.TransactionsByPriceAndNonce, coinbase common.Address, totalUsedMoney *big.Int, cch core.CrossChainHelper) (rmTxs types.Transactions) {
   553  
   554  	gp := new(core.GasPool).AddGas(self.current.header.GasLimit)
   555  
   556  	var coalescedLogs []*types.Log
   557  
   558  	for {
   559  
   560  		if gp.Gas() < params.TxGas {
   561  			self.logger.Trace("Not enough gas for further transactions", "have", gp, "want", params.TxGas)
   562  			break
   563  		}
   564  
   565  		tx := txs.Peek()
   566  		if tx == nil {
   567  			break
   568  		}
   569  
   570  		from, _ := types.Sender(self.current.signer, tx)
   571  
   572  		if tx.Protected() && !self.config.IsEIP155(self.current.header.Number) {
   573  			self.logger.Trace("Ignoring reply protected transaction", "hash", tx.Hash(), "eip155", self.config.EIP155Block)
   574  
   575  			txs.Pop()
   576  			continue
   577  		}
   578  
   579  		self.current.state.Prepare(tx.Hash(), common.Hash{}, self.current.tcount)
   580  
   581  		logs, err := self.commitTransactionEx(tx, coinbase, gp, totalUsedMoney, cch)
   582  		switch err {
   583  		case core.ErrGasLimitReached:
   584  
   585  			self.logger.Trace("Gas limit exceeded for current block", "sender", from)
   586  			txs.Pop()
   587  
   588  		case core.ErrNonceTooLow:
   589  
   590  			self.logger.Trace("Skipping transaction with low nonce", "sender", from, "nonce", tx.Nonce())
   591  			txs.Shift()
   592  
   593  		case core.ErrNonceTooHigh:
   594  
   595  			self.logger.Trace("Skipping account with hight nonce", "sender", from, "nonce", tx.Nonce())
   596  			txs.Pop()
   597  
   598  		case core.ErrInvalidTx4:
   599  
   600  			rmTxs = append(rmTxs, tx)
   601  			self.logger.Trace("Invalid Tx4, this tx will be removed", "hash", tx.Hash())
   602  			txs.Shift()
   603  
   604  		case nil:
   605  
   606  			coalescedLogs = append(coalescedLogs, logs...)
   607  			self.current.tcount++
   608  			txs.Shift()
   609  
   610  		default:
   611  
   612  			self.logger.Debug("Transaction failed, account skipped", "hash", tx.Hash(), "err", err)
   613  			txs.Shift()
   614  		}
   615  	}
   616  
   617  	if len(coalescedLogs) > 0 || self.current.tcount > 0 {
   618  
   619  		cpy := make([]*types.Log, len(coalescedLogs))
   620  		for i, l := range coalescedLogs {
   621  			cpy[i] = new(types.Log)
   622  			*cpy[i] = *l
   623  		}
   624  		go func(logs []*types.Log, tcount int) {
   625  			if len(logs) > 0 {
   626  				self.mux.Post(core.PendingLogsEvent{Logs: logs})
   627  			}
   628  			if tcount > 0 {
   629  				self.mux.Post(core.PendingStateEvent{})
   630  			}
   631  		}(cpy, self.current.tcount)
   632  	}
   633  	return
   634  }
   635  
   636  func (self *worker) commitTransactionEx(tx *types.Transaction, coinbase common.Address, gp *core.GasPool, totalUsedMoney *big.Int, cch core.CrossChainHelper) ([]*types.Log, error) {
   637  	snap := self.current.state.Snapshot()
   638  
   639  	receipt, err := core.ApplyTransactionEx(self.config, self.chain, nil, gp, self.current.state, self.current.ops, self.current.header, tx, &self.current.header.GasUsed, totalUsedMoney, vm.Config{}, cch, true)
   640  	if err != nil {
   641  		self.current.state.RevertToSnapshot(snap)
   642  		return nil, err
   643  	}
   644  
   645  	self.current.txs = append(self.current.txs, tx)
   646  	self.current.receipts = append(self.current.receipts, receipt)
   647  
   648  	return receipt.Logs, nil
   649  }