github.com/ethereum/go-ethereum@v1.14.3/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/ethereum/go-ethereum/common"
    27  	"github.com/ethereum/go-ethereum/consensus/misc/eip1559"
    28  	"github.com/ethereum/go-ethereum/consensus/misc/eip4844"
    29  	"github.com/ethereum/go-ethereum/core"
    30  	"github.com/ethereum/go-ethereum/core/state"
    31  	"github.com/ethereum/go-ethereum/core/txpool"
    32  	"github.com/ethereum/go-ethereum/core/types"
    33  	"github.com/ethereum/go-ethereum/core/vm"
    34  	"github.com/ethereum/go-ethereum/log"
    35  	"github.com/ethereum/go-ethereum/params"
    36  	"github.com/holiman/uint256"
    37  )
    38  
    39  var (
    40  	errBlockInterruptedByNewHead  = errors.New("new head arrived while building block")
    41  	errBlockInterruptedByRecommit = errors.New("recommit interrupt while building block")
    42  	errBlockInterruptedByTimeout  = errors.New("timeout while building block")
    43  )
    44  
    45  // environment is the worker's current environment and holds all
    46  // information of the sealing block generation.
    47  type environment struct {
    48  	signer   types.Signer
    49  	state    *state.StateDB // apply state changes here
    50  	tcount   int            // tx count in cycle
    51  	gasPool  *core.GasPool  // available gas used to pack transactions
    52  	coinbase common.Address
    53  
    54  	header   *types.Header
    55  	txs      []*types.Transaction
    56  	receipts []*types.Receipt
    57  	sidecars []*types.BlobTxSidecar
    58  	blobs    int
    59  }
    60  
    61  const (
    62  	commitInterruptNone int32 = iota
    63  	commitInterruptNewHead
    64  	commitInterruptResubmit
    65  	commitInterruptTimeout
    66  )
    67  
    68  // newPayloadResult is the result of payload generation.
    69  type newPayloadResult struct {
    70  	err      error
    71  	block    *types.Block
    72  	fees     *big.Int               // total block fees
    73  	sidecars []*types.BlobTxSidecar // collected blobs of blob transactions
    74  	stateDB  *state.StateDB         // StateDB after executing the transactions
    75  	receipts []*types.Receipt       // Receipts collected during construction
    76  }
    77  
    78  // generateParams wraps various settings for generating sealing task.
    79  type generateParams struct {
    80  	timestamp   uint64            // The timestamp for sealing task
    81  	forceTime   bool              // Flag whether the given timestamp is immutable or not
    82  	parentHash  common.Hash       // Parent block hash, empty means the latest chain head
    83  	coinbase    common.Address    // The fee recipient address for including transaction
    84  	random      common.Hash       // The randomness generated by beacon chain, empty before the merge
    85  	withdrawals types.Withdrawals // List of withdrawals to include in block (shanghai field)
    86  	beaconRoot  *common.Hash      // The beacon root (cancun field).
    87  	noTxs       bool              // Flag whether an empty block without any transaction is expected
    88  }
    89  
    90  // generateWork generates a sealing block based on the given parameters.
    91  func (miner *Miner) generateWork(params *generateParams) *newPayloadResult {
    92  	work, err := miner.prepareWork(params)
    93  	if err != nil {
    94  		return &newPayloadResult{err: err}
    95  	}
    96  	if !params.noTxs {
    97  		interrupt := new(atomic.Int32)
    98  		timer := time.AfterFunc(miner.config.Recommit, func() {
    99  			interrupt.Store(commitInterruptTimeout)
   100  		})
   101  		defer timer.Stop()
   102  
   103  		err := miner.fillTransactions(interrupt, work)
   104  		if errors.Is(err, errBlockInterruptedByTimeout) {
   105  			log.Warn("Block building is interrupted", "allowance", common.PrettyDuration(miner.config.Recommit))
   106  		}
   107  	}
   108  	body := types.Body{Transactions: work.txs, Withdrawals: params.withdrawals}
   109  	block, err := miner.engine.FinalizeAndAssemble(miner.chain, work.header, work.state, &body, work.receipts)
   110  	if err != nil {
   111  		return &newPayloadResult{err: err}
   112  	}
   113  	return &newPayloadResult{
   114  		block:    block,
   115  		fees:     totalFees(block, work.receipts),
   116  		sidecars: work.sidecars,
   117  		stateDB:  work.state,
   118  		receipts: work.receipts,
   119  	}
   120  }
   121  
   122  // prepareWork constructs the sealing task according to the given parameters,
   123  // either based on the last chain head or specified parent. In this function
   124  // the pending transactions are not filled yet, only the empty task returned.
   125  func (miner *Miner) prepareWork(genParams *generateParams) (*environment, error) {
   126  	miner.confMu.RLock()
   127  	defer miner.confMu.RUnlock()
   128  
   129  	// Find the parent block for sealing task
   130  	parent := miner.chain.CurrentBlock()
   131  	if genParams.parentHash != (common.Hash{}) {
   132  		block := miner.chain.GetBlockByHash(genParams.parentHash)
   133  		if block == nil {
   134  			return nil, errors.New("missing parent")
   135  		}
   136  		parent = block.Header()
   137  	}
   138  	// Sanity check the timestamp correctness, recap the timestamp
   139  	// to parent+1 if the mutation is allowed.
   140  	timestamp := genParams.timestamp
   141  	if parent.Time >= timestamp {
   142  		if genParams.forceTime {
   143  			return nil, fmt.Errorf("invalid timestamp, parent %d given %d", parent.Time, timestamp)
   144  		}
   145  		timestamp = parent.Time + 1
   146  	}
   147  	// Construct the sealing block header.
   148  	header := &types.Header{
   149  		ParentHash: parent.Hash(),
   150  		Number:     new(big.Int).Add(parent.Number, common.Big1),
   151  		GasLimit:   core.CalcGasLimit(parent.GasLimit, miner.config.GasCeil),
   152  		Time:       timestamp,
   153  		Coinbase:   genParams.coinbase,
   154  	}
   155  	// Set the extra field.
   156  	if len(miner.config.ExtraData) != 0 {
   157  		header.Extra = miner.config.ExtraData
   158  	}
   159  	// Set the randomness field from the beacon chain if it's available.
   160  	if genParams.random != (common.Hash{}) {
   161  		header.MixDigest = genParams.random
   162  	}
   163  	// Set baseFee and GasLimit if we are on an EIP-1559 chain
   164  	if miner.chainConfig.IsLondon(header.Number) {
   165  		header.BaseFee = eip1559.CalcBaseFee(miner.chainConfig, parent)
   166  		if !miner.chainConfig.IsLondon(parent.Number) {
   167  			parentGasLimit := parent.GasLimit * miner.chainConfig.ElasticityMultiplier()
   168  			header.GasLimit = core.CalcGasLimit(parentGasLimit, miner.config.GasCeil)
   169  		}
   170  	}
   171  	// Run the consensus preparation with the default or customized consensus engine.
   172  	// Note that the `header.Time` may be changed.
   173  	if err := miner.engine.Prepare(miner.chain, header); err != nil {
   174  		log.Error("Failed to prepare header for sealing", "err", err)
   175  		return nil, err
   176  	}
   177  	// Apply EIP-4844, EIP-4788.
   178  	if miner.chainConfig.IsCancun(header.Number, header.Time) {
   179  		var excessBlobGas uint64
   180  		if miner.chainConfig.IsCancun(parent.Number, parent.Time) {
   181  			excessBlobGas = eip4844.CalcExcessBlobGas(*parent.ExcessBlobGas, *parent.BlobGasUsed)
   182  		} else {
   183  			// For the first post-fork block, both parent.data_gas_used and parent.excess_data_gas are evaluated as 0
   184  			excessBlobGas = eip4844.CalcExcessBlobGas(0, 0)
   185  		}
   186  		header.BlobGasUsed = new(uint64)
   187  		header.ExcessBlobGas = &excessBlobGas
   188  		header.ParentBeaconRoot = genParams.beaconRoot
   189  	}
   190  	// Could potentially happen if starting to mine in an odd state.
   191  	// Note genParams.coinbase can be different with header.Coinbase
   192  	// since clique algorithm can modify the coinbase field in header.
   193  	env, err := miner.makeEnv(parent, header, genParams.coinbase)
   194  	if err != nil {
   195  		log.Error("Failed to create sealing context", "err", err)
   196  		return nil, err
   197  	}
   198  	if header.ParentBeaconRoot != nil {
   199  		context := core.NewEVMBlockContext(header, miner.chain, nil)
   200  		vmenv := vm.NewEVM(context, vm.TxContext{}, env.state, miner.chainConfig, vm.Config{})
   201  		core.ProcessBeaconBlockRoot(*header.ParentBeaconRoot, vmenv, env.state)
   202  	}
   203  	return env, nil
   204  }
   205  
   206  // makeEnv creates a new environment for the sealing block.
   207  func (miner *Miner) makeEnv(parent *types.Header, header *types.Header, coinbase common.Address) (*environment, error) {
   208  	// Retrieve the parent state to execute on top and start a prefetcher for
   209  	// the miner to speed block sealing up a bit.
   210  	state, err := miner.chain.StateAt(parent.Root)
   211  	if err != nil {
   212  		return nil, err
   213  	}
   214  	// Note the passed coinbase may be different with header.Coinbase.
   215  	return &environment{
   216  		signer:   types.MakeSigner(miner.chainConfig, header.Number, header.Time),
   217  		state:    state,
   218  		coinbase: coinbase,
   219  		header:   header,
   220  	}, nil
   221  }
   222  
   223  func (miner *Miner) commitTransaction(env *environment, tx *types.Transaction) error {
   224  	if tx.Type() == types.BlobTxType {
   225  		return miner.commitBlobTransaction(env, tx)
   226  	}
   227  	receipt, err := miner.applyTransaction(env, tx)
   228  	if err != nil {
   229  		return err
   230  	}
   231  	env.txs = append(env.txs, tx)
   232  	env.receipts = append(env.receipts, receipt)
   233  	env.tcount++
   234  	return nil
   235  }
   236  
   237  func (miner *Miner) commitBlobTransaction(env *environment, tx *types.Transaction) error {
   238  	sc := tx.BlobTxSidecar()
   239  	if sc == nil {
   240  		panic("blob transaction without blobs in miner")
   241  	}
   242  	// Checking against blob gas limit: It's kind of ugly to perform this check here, but there
   243  	// isn't really a better place right now. The blob gas limit is checked at block validation time
   244  	// and not during execution. This means core.ApplyTransaction will not return an error if the
   245  	// tx has too many blobs. So we have to explicitly check it here.
   246  	if (env.blobs+len(sc.Blobs))*params.BlobTxBlobGasPerBlob > params.MaxBlobGasPerBlock {
   247  		return errors.New("max data blobs reached")
   248  	}
   249  	receipt, err := miner.applyTransaction(env, tx)
   250  	if err != nil {
   251  		return err
   252  	}
   253  	env.txs = append(env.txs, tx.WithoutBlobTxSidecar())
   254  	env.receipts = append(env.receipts, receipt)
   255  	env.sidecars = append(env.sidecars, sc)
   256  	env.blobs += len(sc.Blobs)
   257  	*env.header.BlobGasUsed += receipt.BlobGasUsed
   258  	env.tcount++
   259  	return nil
   260  }
   261  
   262  // applyTransaction runs the transaction. If execution fails, state and gas pool are reverted.
   263  func (miner *Miner) applyTransaction(env *environment, tx *types.Transaction) (*types.Receipt, error) {
   264  	var (
   265  		snap = env.state.Snapshot()
   266  		gp   = env.gasPool.Gas()
   267  	)
   268  	receipt, err := core.ApplyTransaction(miner.chainConfig, miner.chain, &env.coinbase, env.gasPool, env.state, env.header, tx, &env.header.GasUsed, vm.Config{})
   269  	if err != nil {
   270  		env.state.RevertToSnapshot(snap)
   271  		env.gasPool.SetGas(gp)
   272  	}
   273  	return receipt, err
   274  }
   275  
   276  func (miner *Miner) commitTransactions(env *environment, plainTxs, blobTxs *transactionsByPriceAndNonce, interrupt *atomic.Int32) error {
   277  	gasLimit := env.header.GasLimit
   278  	if env.gasPool == nil {
   279  		env.gasPool = new(core.GasPool).AddGas(gasLimit)
   280  	}
   281  	for {
   282  		// Check interruption signal and abort building if it's fired.
   283  		if interrupt != nil {
   284  			if signal := interrupt.Load(); signal != commitInterruptNone {
   285  				return signalToErr(signal)
   286  			}
   287  		}
   288  		// If we don't have enough gas for any further transactions then we're done.
   289  		if env.gasPool.Gas() < params.TxGas {
   290  			log.Trace("Not enough gas for further transactions", "have", env.gasPool, "want", params.TxGas)
   291  			break
   292  		}
   293  		// If we don't have enough blob space for any further blob transactions,
   294  		// skip that list altogether
   295  		if !blobTxs.Empty() && env.blobs*params.BlobTxBlobGasPerBlob >= params.MaxBlobGasPerBlock {
   296  			log.Trace("Not enough blob space for further blob transactions")
   297  			blobTxs.Clear()
   298  			// Fall though to pick up any plain txs
   299  		}
   300  		// Retrieve the next transaction and abort if all done.
   301  		var (
   302  			ltx *txpool.LazyTransaction
   303  			txs *transactionsByPriceAndNonce
   304  		)
   305  		pltx, ptip := plainTxs.Peek()
   306  		bltx, btip := blobTxs.Peek()
   307  
   308  		switch {
   309  		case pltx == nil:
   310  			txs, ltx = blobTxs, bltx
   311  		case bltx == nil:
   312  			txs, ltx = plainTxs, pltx
   313  		default:
   314  			if ptip.Lt(btip) {
   315  				txs, ltx = blobTxs, bltx
   316  			} else {
   317  				txs, ltx = plainTxs, pltx
   318  			}
   319  		}
   320  		if ltx == nil {
   321  			break
   322  		}
   323  		// If we don't have enough space for the next transaction, skip the account.
   324  		if env.gasPool.Gas() < ltx.Gas {
   325  			log.Trace("Not enough gas left for transaction", "hash", ltx.Hash, "left", env.gasPool.Gas(), "needed", ltx.Gas)
   326  			txs.Pop()
   327  			continue
   328  		}
   329  		if left := uint64(params.MaxBlobGasPerBlock - env.blobs*params.BlobTxBlobGasPerBlob); left < ltx.BlobGas {
   330  			log.Trace("Not enough blob gas left for transaction", "hash", ltx.Hash, "left", left, "needed", ltx.BlobGas)
   331  			txs.Pop()
   332  			continue
   333  		}
   334  		// Transaction seems to fit, pull it up from the pool
   335  		tx := ltx.Resolve()
   336  		if tx == nil {
   337  			log.Trace("Ignoring evicted transaction", "hash", ltx.Hash)
   338  			txs.Pop()
   339  			continue
   340  		}
   341  		// Error may be ignored here. The error has already been checked
   342  		// during transaction acceptance in the transaction pool.
   343  		from, _ := types.Sender(env.signer, tx)
   344  
   345  		// Check whether the tx is replay protected. If we're not in the EIP155 hf
   346  		// phase, start ignoring the sender until we do.
   347  		if tx.Protected() && !miner.chainConfig.IsEIP155(env.header.Number) {
   348  			log.Trace("Ignoring replay protected transaction", "hash", ltx.Hash, "eip155", miner.chainConfig.EIP155Block)
   349  			txs.Pop()
   350  			continue
   351  		}
   352  		// Start executing the transaction
   353  		env.state.SetTxContext(tx.Hash(), env.tcount)
   354  
   355  		err := miner.commitTransaction(env, tx)
   356  		switch {
   357  		case errors.Is(err, core.ErrNonceTooLow):
   358  			// New head notification data race between the transaction pool and miner, shift
   359  			log.Trace("Skipping transaction with low nonce", "hash", ltx.Hash, "sender", from, "nonce", tx.Nonce())
   360  			txs.Shift()
   361  
   362  		case errors.Is(err, nil):
   363  			// Everything ok, collect the logs and shift in the next transaction from the same account
   364  			txs.Shift()
   365  
   366  		default:
   367  			// Transaction is regarded as invalid, drop all consecutive transactions from
   368  			// the same sender because of `nonce-too-high` clause.
   369  			log.Debug("Transaction failed, account skipped", "hash", ltx.Hash, "err", err)
   370  			txs.Pop()
   371  		}
   372  	}
   373  	return nil
   374  }
   375  
   376  // fillTransactions retrieves the pending transactions from the txpool and fills them
   377  // into the given sealing block. The transaction selection and ordering strategy can
   378  // be customized with the plugin in the future.
   379  func (miner *Miner) fillTransactions(interrupt *atomic.Int32, env *environment) error {
   380  	miner.confMu.RLock()
   381  	tip := miner.config.GasPrice
   382  	miner.confMu.RUnlock()
   383  
   384  	// Retrieve the pending transactions pre-filtered by the 1559/4844 dynamic fees
   385  	filter := txpool.PendingFilter{
   386  		MinTip: uint256.MustFromBig(tip),
   387  	}
   388  	if env.header.BaseFee != nil {
   389  		filter.BaseFee = uint256.MustFromBig(env.header.BaseFee)
   390  	}
   391  	if env.header.ExcessBlobGas != nil {
   392  		filter.BlobFee = uint256.MustFromBig(eip4844.CalcBlobFee(*env.header.ExcessBlobGas))
   393  	}
   394  	filter.OnlyPlainTxs, filter.OnlyBlobTxs = true, false
   395  	pendingPlainTxs := miner.txpool.Pending(filter)
   396  
   397  	filter.OnlyPlainTxs, filter.OnlyBlobTxs = false, true
   398  	pendingBlobTxs := miner.txpool.Pending(filter)
   399  
   400  	// Split the pending transactions into locals and remotes.
   401  	localPlainTxs, remotePlainTxs := make(map[common.Address][]*txpool.LazyTransaction), pendingPlainTxs
   402  	localBlobTxs, remoteBlobTxs := make(map[common.Address][]*txpool.LazyTransaction), pendingBlobTxs
   403  
   404  	for _, account := range miner.txpool.Locals() {
   405  		if txs := remotePlainTxs[account]; len(txs) > 0 {
   406  			delete(remotePlainTxs, account)
   407  			localPlainTxs[account] = txs
   408  		}
   409  		if txs := remoteBlobTxs[account]; len(txs) > 0 {
   410  			delete(remoteBlobTxs, account)
   411  			localBlobTxs[account] = txs
   412  		}
   413  	}
   414  	// Fill the block with all available pending transactions.
   415  	if len(localPlainTxs) > 0 || len(localBlobTxs) > 0 {
   416  		plainTxs := newTransactionsByPriceAndNonce(env.signer, localPlainTxs, env.header.BaseFee)
   417  		blobTxs := newTransactionsByPriceAndNonce(env.signer, localBlobTxs, env.header.BaseFee)
   418  
   419  		if err := miner.commitTransactions(env, plainTxs, blobTxs, interrupt); err != nil {
   420  			return err
   421  		}
   422  	}
   423  	if len(remotePlainTxs) > 0 || len(remoteBlobTxs) > 0 {
   424  		plainTxs := newTransactionsByPriceAndNonce(env.signer, remotePlainTxs, env.header.BaseFee)
   425  		blobTxs := newTransactionsByPriceAndNonce(env.signer, remoteBlobTxs, env.header.BaseFee)
   426  
   427  		if err := miner.commitTransactions(env, plainTxs, blobTxs, interrupt); err != nil {
   428  			return err
   429  		}
   430  	}
   431  	return nil
   432  }
   433  
   434  // totalFees computes total consumed miner fees in Wei. Block transactions and receipts have to have the same order.
   435  func totalFees(block *types.Block, receipts []*types.Receipt) *big.Int {
   436  	feesWei := new(big.Int)
   437  	for i, tx := range block.Transactions() {
   438  		minerFee, _ := tx.EffectiveGasTip(block.BaseFee())
   439  		feesWei.Add(feesWei, new(big.Int).Mul(new(big.Int).SetUint64(receipts[i].GasUsed), minerFee))
   440  	}
   441  	return feesWei
   442  }
   443  
   444  // signalToErr converts the interruption signal to a concrete error type for return.
   445  // The given signal must be a valid interruption signal.
   446  func signalToErr(signal int32) error {
   447  	switch signal {
   448  	case commitInterruptNewHead:
   449  		return errBlockInterruptedByNewHead
   450  	case commitInterruptResubmit:
   451  		return errBlockInterruptedByRecommit
   452  	case commitInterruptTimeout:
   453  		return errBlockInterruptedByTimeout
   454  	default:
   455  		panic(fmt.Errorf("undefined signal %d", signal))
   456  	}
   457  }