github.com/theQRL/go-zond@v0.2.1/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  	"errors"
    21  	"fmt"
    22  	"math/big"
    23  	"sync/atomic"
    24  	"time"
    25  
    26  	"github.com/theQRL/go-zond/common"
    27  	"github.com/theQRL/go-zond/consensus/misc/eip1559"
    28  	"github.com/theQRL/go-zond/core"
    29  	"github.com/theQRL/go-zond/core/state"
    30  	"github.com/theQRL/go-zond/core/txpool"
    31  	"github.com/theQRL/go-zond/core/types"
    32  	"github.com/theQRL/go-zond/core/vm"
    33  	"github.com/theQRL/go-zond/log"
    34  	"github.com/theQRL/go-zond/params"
    35  )
    36  
    37  var (
    38  	errBlockInterruptedByNewHead  = errors.New("new head arrived while building block")
    39  	errBlockInterruptedByRecommit = errors.New("recommit interrupt while building block")
    40  	errBlockInterruptedByTimeout  = errors.New("timeout while building block")
    41  )
    42  
    43  // environment is the worker's current environment and holds all
    44  // information of the sealing block generation.
    45  type environment struct {
    46  	signer   types.Signer
    47  	state    *state.StateDB // apply state changes here
    48  	tcount   int            // tx count in cycle
    49  	gasPool  *core.GasPool  // available gas used to pack transactions
    50  	coinbase common.Address
    51  
    52  	header   *types.Header
    53  	txs      []*types.Transaction
    54  	receipts []*types.Receipt
    55  }
    56  
    57  const (
    58  	commitInterruptNone int32 = iota
    59  	commitInterruptNewHead
    60  	commitInterruptResubmit
    61  	commitInterruptTimeout
    62  )
    63  
    64  // newPayloadResult is the result of payload generation.
    65  type newPayloadResult struct {
    66  	err      error
    67  	block    *types.Block
    68  	fees     *big.Int         // total block fees
    69  	stateDB  *state.StateDB   // StateDB after executing the transactions
    70  	receipts []*types.Receipt // Receipts collected during construction
    71  }
    72  
    73  // generateParams wraps various settings for generating sealing task.
    74  type generateParams struct {
    75  	timestamp   uint64            // The timestamp for sealing task
    76  	forceTime   bool              // Flag whether the given timestamp is immutable or not
    77  	parentHash  common.Hash       // Parent block hash, empty means the latest chain head
    78  	coinbase    common.Address    // The fee recipient address for including transaction
    79  	random      common.Hash       // The randomness generated by beacon chain, empty before the merge
    80  	withdrawals types.Withdrawals // List of withdrawals to include in block.
    81  	noTxs       bool              // Flag whether an empty block without any transaction is expected
    82  }
    83  
    84  // generateWork generates a sealing block based on the given parameters.
    85  func (miner *Miner) generateWork(params *generateParams) *newPayloadResult {
    86  	work, err := miner.prepareWork(params)
    87  	if err != nil {
    88  		return &newPayloadResult{err: err}
    89  	}
    90  	if !params.noTxs {
    91  		interrupt := new(atomic.Int32)
    92  		timer := time.AfterFunc(miner.config.Recommit, func() {
    93  			interrupt.Store(commitInterruptTimeout)
    94  		})
    95  		defer timer.Stop()
    96  
    97  		err := miner.fillTransactions(interrupt, work)
    98  		if errors.Is(err, errBlockInterruptedByTimeout) {
    99  			log.Warn("Block building is interrupted", "allowance", common.PrettyDuration(miner.config.Recommit))
   100  		}
   101  	}
   102  	body := types.Body{Transactions: work.txs, Withdrawals: params.withdrawals}
   103  	block, err := miner.engine.FinalizeAndAssemble(miner.chain, work.header, work.state, &body, work.receipts)
   104  	if err != nil {
   105  		return &newPayloadResult{err: err}
   106  	}
   107  	return &newPayloadResult{
   108  		block:    block,
   109  		fees:     totalFees(block, work.receipts),
   110  		stateDB:  work.state,
   111  		receipts: work.receipts,
   112  	}
   113  }
   114  
   115  // prepareWork constructs the sealing task according to the given parameters,
   116  // either based on the last chain head or specified parent. In this function
   117  // the pending transactions are not filled yet, only the empty task returned.
   118  func (miner *Miner) prepareWork(genParams *generateParams) (*environment, error) {
   119  	miner.confMu.RLock()
   120  	defer miner.confMu.RUnlock()
   121  
   122  	// Find the parent block for sealing task
   123  	parent := miner.chain.CurrentBlock()
   124  	if genParams.parentHash != (common.Hash{}) {
   125  		block := miner.chain.GetBlockByHash(genParams.parentHash)
   126  		if block == nil {
   127  			return nil, errors.New("missing parent")
   128  		}
   129  		parent = block.Header()
   130  	}
   131  	// Sanity check the timestamp correctness, recap the timestamp
   132  	// to parent+1 if the mutation is allowed.
   133  	timestamp := genParams.timestamp
   134  	if parent.Time >= timestamp {
   135  		if genParams.forceTime {
   136  			return nil, fmt.Errorf("invalid timestamp, parent %d given %d", parent.Time, timestamp)
   137  		}
   138  		timestamp = parent.Time + 1
   139  	}
   140  	// Construct the sealing block header.
   141  	header := &types.Header{
   142  		ParentHash: parent.Hash(),
   143  		Number:     new(big.Int).Add(parent.Number, common.Big1),
   144  		GasLimit:   params.MaxGasLimit,
   145  		Time:       timestamp,
   146  		Coinbase:   genParams.coinbase,
   147  	}
   148  	// Set the extra field.
   149  	if len(miner.config.ExtraData) != 0 {
   150  		header.Extra = miner.config.ExtraData
   151  	}
   152  	// Set the randomness field from the beacon chain if it's available.
   153  	if genParams.random != (common.Hash{}) {
   154  		header.Random = genParams.random
   155  	}
   156  	// Set baseFee and GasLimit if we are on an EIP-1559 chain
   157  	header.BaseFee = eip1559.CalcBaseFee(miner.chainConfig, parent)
   158  
   159  	// Could potentially happen if starting to mine in an odd state..
   160  	env, err := miner.makeEnv(parent, header, genParams.coinbase)
   161  	if err != nil {
   162  		log.Error("Failed to create sealing context", "err", err)
   163  		return nil, err
   164  	}
   165  	return env, nil
   166  }
   167  
   168  // makeEnv creates a new environment for the sealing block.
   169  func (miner *Miner) makeEnv(parent *types.Header, header *types.Header, coinbase common.Address) (*environment, error) {
   170  	// Retrieve the parent state to execute on top.
   171  	state, err := miner.chain.StateAt(parent.Root)
   172  	if err != nil {
   173  		return nil, err
   174  	}
   175  	// Note the passed coinbase may be different with header.Coinbase.
   176  	return &environment{
   177  		signer:   types.MakeSigner(miner.chainConfig),
   178  		state:    state,
   179  		coinbase: coinbase,
   180  		header:   header,
   181  	}, nil
   182  }
   183  
   184  func (miner *Miner) commitTransaction(env *environment, tx *types.Transaction) error {
   185  	receipt, err := miner.applyTransaction(env, tx)
   186  	if err != nil {
   187  		return err
   188  	}
   189  	env.txs = append(env.txs, tx)
   190  	env.receipts = append(env.receipts, receipt)
   191  	env.tcount++
   192  	return nil
   193  }
   194  
   195  // applyTransaction runs the transaction. If execution fails, state and gas pool are reverted.
   196  func (miner *Miner) applyTransaction(env *environment, tx *types.Transaction) (*types.Receipt, error) {
   197  	var (
   198  		snap = env.state.Snapshot()
   199  		gp   = env.gasPool.Gas()
   200  	)
   201  	receipt, err := core.ApplyTransaction(miner.chainConfig, miner.chain, &env.coinbase, env.gasPool, env.state, env.header, tx, &env.header.GasUsed, vm.Config{})
   202  	if err != nil {
   203  		env.state.RevertToSnapshot(snap)
   204  		env.gasPool.SetGas(gp)
   205  	}
   206  	return receipt, err
   207  }
   208  
   209  func (miner *Miner) commitTransactions(env *environment, txs *transactionsByPriceAndNonce, interrupt *atomic.Int32) error {
   210  	gasLimit := env.header.GasLimit
   211  	if env.gasPool == nil {
   212  		env.gasPool = new(core.GasPool).AddGas(gasLimit)
   213  	}
   214  
   215  	for {
   216  		// Check interruption signal and abort building if it's fired.
   217  		if interrupt != nil {
   218  			if signal := interrupt.Load(); signal != commitInterruptNone {
   219  				return signalToErr(signal)
   220  			}
   221  		}
   222  		// If we don't have enough gas for any further transactions then we're done.
   223  		if env.gasPool.Gas() < params.TxGas {
   224  			log.Trace("Not enough gas for further transactions", "have", env.gasPool, "want", params.TxGas)
   225  			break
   226  		}
   227  		// Retrieve the next transaction and abort if all done.
   228  		ltx, _ := txs.Peek()
   229  		if ltx == nil {
   230  			break
   231  		}
   232  		// If we don't have enough space for the next transaction, skip the account.
   233  		if env.gasPool.Gas() < ltx.Gas {
   234  			log.Trace("Not enough gas left for transaction", "hash", ltx.Hash, "left", env.gasPool.Gas(), "needed", ltx.Gas)
   235  			txs.Pop()
   236  			continue
   237  		}
   238  
   239  		// Transaction seems to fit, pull it up from the pool
   240  		tx := ltx.Resolve()
   241  		if tx == nil {
   242  			log.Trace("Ignoring evicted transaction", "hash", ltx.Hash)
   243  			txs.Pop()
   244  			continue
   245  		}
   246  
   247  		// Error may be ignored here. The error has already been checked
   248  		// during transaction acceptance in the transaction pool.
   249  		from, _ := types.Sender(env.signer, tx)
   250  
   251  		// Start executing the transaction
   252  		env.state.SetTxContext(tx.Hash(), env.tcount)
   253  
   254  		err := miner.commitTransaction(env, tx)
   255  		switch {
   256  		case errors.Is(err, core.ErrNonceTooLow):
   257  			// New head notification data race between the transaction pool and miner, shift
   258  			log.Trace("Skipping transaction with low nonce", "hash", ltx.Hash, "sender", from, "nonce", tx.Nonce())
   259  			txs.Shift()
   260  
   261  		case errors.Is(err, nil):
   262  			// Everything ok, collect the logs and shift in the next transaction from the same account
   263  			txs.Shift()
   264  
   265  		default:
   266  			// Transaction is regarded as invalid, drop all consecutive transactions from
   267  			// the same sender because of `nonce-too-high` clause.
   268  			log.Debug("Transaction failed, account skipped", "hash", ltx.Hash, "err", err)
   269  			txs.Pop()
   270  		}
   271  	}
   272  
   273  	return nil
   274  }
   275  
   276  // fillTransactions retrieves the pending transactions from the txpool and fills them
   277  // into the given sealing block. The transaction selection and ordering strategy can
   278  // be customized with the plugin in the future.
   279  func (miner *Miner) fillTransactions(interrupt *atomic.Int32, env *environment) error {
   280  	miner.confMu.RLock()
   281  	tip := miner.config.GasPrice
   282  	miner.confMu.RUnlock()
   283  
   284  	// Retrieve the pending transactions pre-filtered by the 1559 dynamic fees
   285  	filter := txpool.PendingFilter{
   286  		MinTip: tip,
   287  	}
   288  	if env.header.BaseFee != nil {
   289  		filter.BaseFee = env.header.BaseFee
   290  	}
   291  	pendingTxs := miner.txpool.Pending(filter)
   292  
   293  	// Split the pending transactions into locals and remotes.
   294  	localTxs, remoteTxs := make(map[common.Address][]*txpool.LazyTransaction), pendingTxs
   295  	for _, account := range miner.txpool.Locals() {
   296  		if txs := remoteTxs[account]; len(txs) > 0 {
   297  			delete(remoteTxs, account)
   298  			localTxs[account] = txs
   299  		}
   300  	}
   301  	// Fill the block with all available pending transactions.
   302  	if len(localTxs) > 0 {
   303  		txs := newTransactionsByPriceAndNonce(env.signer, localTxs, env.header.BaseFee)
   304  		if err := miner.commitTransactions(env, txs, interrupt); err != nil {
   305  			return err
   306  		}
   307  	}
   308  	if len(remoteTxs) > 0 {
   309  		txs := newTransactionsByPriceAndNonce(env.signer, remoteTxs, env.header.BaseFee)
   310  		if err := miner.commitTransactions(env, txs, interrupt); err != nil {
   311  			return err
   312  		}
   313  	}
   314  	return nil
   315  }
   316  
   317  // totalFees computes total consumed miner fees in Wei. Block transactions and receipts have to have the same order.
   318  func totalFees(block *types.Block, receipts []*types.Receipt) *big.Int {
   319  	feesWei := new(big.Int)
   320  	for i, tx := range block.Transactions() {
   321  		minerFee, _ := tx.EffectiveGasTip(block.BaseFee())
   322  		feesWei.Add(feesWei, new(big.Int).Mul(new(big.Int).SetUint64(receipts[i].GasUsed), minerFee))
   323  	}
   324  	return feesWei
   325  }
   326  
   327  // signalToErr converts the interruption signal to a concrete error type for return.
   328  // The given signal must be a valid interruption signal.
   329  func signalToErr(signal int32) error {
   330  	switch signal {
   331  	case commitInterruptNewHead:
   332  		return errBlockInterruptedByNewHead
   333  	case commitInterruptResubmit:
   334  		return errBlockInterruptedByRecommit
   335  	case commitInterruptTimeout:
   336  		return errBlockInterruptedByTimeout
   337  	default:
   338  		panic(fmt.Errorf("undefined signal %d", signal))
   339  	}
   340  }