github.com/MetalBlockchain/subnet-evm@v0.6.3/miner/worker.go (about)

     1  // (c) 2019-2020, Ava Labs, Inc.
     2  //
     3  // This file is a derived work, based on the go-ethereum library whose original
     4  // notices appear below.
     5  //
     6  // It is distributed under a license compatible with the licensing terms of the
     7  // original code from which it is derived.
     8  //
     9  // Much love to the original authors for their work.
    10  // **********
    11  // Copyright 2015 The go-ethereum Authors
    12  // This file is part of the go-ethereum library.
    13  //
    14  // The go-ethereum library is free software: you can redistribute it and/or modify
    15  // it under the terms of the GNU Lesser General Public License as published by
    16  // the Free Software Foundation, either version 3 of the License, or
    17  // (at your option) any later version.
    18  //
    19  // The go-ethereum library is distributed in the hope that it will be useful,
    20  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    21  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    22  // GNU Lesser General Public License for more details.
    23  //
    24  // You should have received a copy of the GNU Lesser General Public License
    25  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    26  //
    27  // NOTE: this piece of code is modified by Ted Yin.
    28  // The modification is also licensed under the same LGPL.
    29  
    30  package miner
    31  
    32  import (
    33  	"errors"
    34  	"fmt"
    35  	"math/big"
    36  	"sync"
    37  	"time"
    38  
    39  	"github.com/MetalBlockchain/metalgo/utils/timer/mockable"
    40  	"github.com/MetalBlockchain/metalgo/utils/units"
    41  	"github.com/MetalBlockchain/subnet-evm/consensus"
    42  	"github.com/MetalBlockchain/subnet-evm/consensus/dummy"
    43  	"github.com/MetalBlockchain/subnet-evm/core"
    44  	"github.com/MetalBlockchain/subnet-evm/core/state"
    45  	"github.com/MetalBlockchain/subnet-evm/core/txpool"
    46  	"github.com/MetalBlockchain/subnet-evm/core/types"
    47  	"github.com/MetalBlockchain/subnet-evm/core/vm"
    48  	"github.com/MetalBlockchain/subnet-evm/params"
    49  	"github.com/MetalBlockchain/subnet-evm/precompile/precompileconfig"
    50  	"github.com/MetalBlockchain/subnet-evm/predicate"
    51  	"github.com/ethereum/go-ethereum/common"
    52  	"github.com/ethereum/go-ethereum/event"
    53  	"github.com/ethereum/go-ethereum/log"
    54  )
    55  
    56  const (
    57  	targetTxsSize = 1800 * units.KiB
    58  )
    59  
    60  // environment is the worker's current environment and holds all of the current state information.
    61  type environment struct {
    62  	signer  types.Signer
    63  	state   *state.StateDB // apply state changes here
    64  	tcount  int            // tx count in cycle
    65  	gasPool *core.GasPool  // available gas used to pack transactions
    66  
    67  	parent   *types.Header
    68  	header   *types.Header
    69  	txs      []*types.Transaction
    70  	receipts []*types.Receipt
    71  	size     uint64
    72  
    73  	rules            params.Rules
    74  	predicateContext *precompileconfig.PredicateContext
    75  	// predicateResults contains the results of checking the predicates for each transaction in the miner.
    76  	// The results are accumulated as transactions are executed by the miner and set on the BlockContext.
    77  	// If a transaction is dropped, its results must explicitly be removed from predicateResults in the same
    78  	// way that the gas pool and state is reset.
    79  	predicateResults *predicate.Results
    80  
    81  	start time.Time // Time that block building began
    82  }
    83  
    84  // worker is the main object which takes care of submitting new work to consensus engine
    85  // and gathering the sealing result.
    86  type worker struct {
    87  	config      *Config
    88  	chainConfig *params.ChainConfig
    89  	engine      consensus.Engine
    90  	eth         Backend
    91  	chain       *core.BlockChain
    92  
    93  	// Feeds
    94  	// TODO remove since this will never be written to
    95  	pendingLogsFeed event.Feed
    96  
    97  	// Subscriptions
    98  	mux      *event.TypeMux // TODO replace
    99  	mu       sync.RWMutex   // The lock used to protect the coinbase and extra fields
   100  	coinbase common.Address
   101  	clock    *mockable.Clock // Allows us mock the clock for testing
   102  }
   103  
   104  func newWorker(config *Config, chainConfig *params.ChainConfig, engine consensus.Engine, eth Backend, mux *event.TypeMux, clock *mockable.Clock) *worker {
   105  	worker := &worker{
   106  		config:      config,
   107  		chainConfig: chainConfig,
   108  		engine:      engine,
   109  		eth:         eth,
   110  		chain:       eth.BlockChain(),
   111  		mux:         mux,
   112  		coinbase:    config.Etherbase,
   113  		clock:       clock,
   114  	}
   115  
   116  	return worker
   117  }
   118  
   119  // setEtherbase sets the etherbase used to initialize the block coinbase field.
   120  func (w *worker) setEtherbase(addr common.Address) {
   121  	w.mu.Lock()
   122  	defer w.mu.Unlock()
   123  	w.coinbase = addr
   124  }
   125  
   126  // commitNewWork generates several new sealing tasks based on the parent block.
   127  func (w *worker) commitNewWork(predicateContext *precompileconfig.PredicateContext) (*types.Block, error) {
   128  	w.mu.RLock()
   129  	defer w.mu.RUnlock()
   130  
   131  	tstart := w.clock.Time()
   132  	timestamp := uint64(tstart.Unix())
   133  	parent := w.chain.CurrentBlock()
   134  	// Note: in order to support asynchronous block production, blocks are allowed to have
   135  	// the same timestamp as their parent. This allows more than one block to be produced
   136  	// per second.
   137  	if parent.Time >= timestamp {
   138  		timestamp = parent.Time
   139  	}
   140  
   141  	var gasLimit uint64
   142  	// The fee manager relies on the state of the parent block to set the fee config
   143  	// because the fee config may be changed by the current block.
   144  	feeConfig, _, err := w.chain.GetFeeConfigAt(parent)
   145  	if err != nil {
   146  		return nil, err
   147  	}
   148  	configuredGasLimit := feeConfig.GasLimit.Uint64()
   149  	if w.chainConfig.IsSubnetEVM(timestamp) {
   150  		gasLimit = configuredGasLimit
   151  	} else {
   152  		// The gas limit is set in SubnetEVMGasLimit because the ceiling and floor were set to the same value
   153  		// such that the gas limit converged to it. Since this is hardbaked now, we remove the ability to configure it.
   154  		gasLimit = core.CalcGasLimit(parent.GasUsed, parent.GasLimit, configuredGasLimit, configuredGasLimit)
   155  	}
   156  	header := &types.Header{
   157  		ParentHash: parent.Hash(),
   158  		Number:     new(big.Int).Add(parent.Number, common.Big1),
   159  		GasLimit:   gasLimit,
   160  		Extra:      nil,
   161  		Time:       timestamp,
   162  	}
   163  
   164  	if w.chainConfig.IsSubnetEVM(timestamp) {
   165  		var err error
   166  		header.Extra, header.BaseFee, err = dummy.CalcBaseFee(w.chainConfig, feeConfig, parent, timestamp)
   167  		if err != nil {
   168  			return nil, fmt.Errorf("failed to calculate new base fee: %w", err)
   169  		}
   170  	}
   171  
   172  	if w.coinbase == (common.Address{}) {
   173  		return nil, errors.New("cannot mine without etherbase")
   174  	}
   175  	header.Coinbase = w.coinbase
   176  
   177  	configuredCoinbase, isAllowFeeRecipient, err := w.chain.GetCoinbaseAt(parent)
   178  	if err != nil {
   179  		return nil, fmt.Errorf("failed to get configured coinbase: %w", err)
   180  	}
   181  
   182  	// if fee recipients are not allowed, then the coinbase is the configured coinbase
   183  	// don't set w.coinbase directly to the configured coinbase because that would override the
   184  	// coinbase set by the user
   185  	if !isAllowFeeRecipient && w.coinbase != configuredCoinbase {
   186  		log.Info("fee recipients are not allowed, using required coinbase for the mining", "currentminer", w.coinbase, "required", configuredCoinbase)
   187  		header.Coinbase = configuredCoinbase
   188  	}
   189  
   190  	if err := w.engine.Prepare(w.chain, header); err != nil {
   191  		return nil, fmt.Errorf("failed to prepare header for mining: %w", err)
   192  	}
   193  
   194  	env, err := w.createCurrentEnvironment(predicateContext, parent, header, tstart)
   195  	if err != nil {
   196  		return nil, fmt.Errorf("failed to create new current environment: %w", err)
   197  	}
   198  	// Ensure we always stop prefetcher after block building is complete.
   199  	defer func() {
   200  		if env.state == nil {
   201  			return
   202  		}
   203  		env.state.StopPrefetcher()
   204  	}()
   205  	// Configure any upgrades that should go into effect during this block.
   206  	err = core.ApplyUpgrades(w.chainConfig, &parent.Time, types.NewBlockWithHeader(header), env.state)
   207  	if err != nil {
   208  		log.Error("failed to configure precompiles mining new block", "parent", parent.Hash(), "number", header.Number, "timestamp", header.Time, "err", err)
   209  		return nil, err
   210  	}
   211  
   212  	// Fill the block with all available pending transactions.
   213  	pending := w.eth.TxPool().PendingWithBaseFee(true, header.BaseFee)
   214  
   215  	// Split the pending transactions into locals and remotes
   216  	localTxs := make(map[common.Address][]*txpool.LazyTransaction)
   217  	remoteTxs := pending
   218  	for _, account := range w.eth.TxPool().Locals() {
   219  		if txs := remoteTxs[account]; len(txs) > 0 {
   220  			delete(remoteTxs, account)
   221  			localTxs[account] = txs
   222  		}
   223  	}
   224  	if len(localTxs) > 0 {
   225  		txs := newTransactionsByPriceAndNonce(env.signer, localTxs, header.BaseFee)
   226  		w.commitTransactions(env, txs, header.Coinbase)
   227  	}
   228  	if len(remoteTxs) > 0 {
   229  		txs := newTransactionsByPriceAndNonce(env.signer, remoteTxs, header.BaseFee)
   230  		w.commitTransactions(env, txs, header.Coinbase)
   231  	}
   232  
   233  	return w.commit(env)
   234  }
   235  
   236  func (w *worker) createCurrentEnvironment(predicateContext *precompileconfig.PredicateContext, parent *types.Header, header *types.Header, tstart time.Time) (*environment, error) {
   237  	state, err := w.chain.StateAt(parent.Root)
   238  	if err != nil {
   239  		return nil, err
   240  	}
   241  	state.StartPrefetcher("miner", w.eth.BlockChain().CacheConfig().TriePrefetcherParallelism)
   242  	return &environment{
   243  		signer:           types.MakeSigner(w.chainConfig, header.Number, header.Time),
   244  		state:            state,
   245  		parent:           parent,
   246  		header:           header,
   247  		tcount:           0,
   248  		gasPool:          new(core.GasPool).AddGas(header.GasLimit),
   249  		rules:            w.chainConfig.Rules(header.Number, header.Time),
   250  		predicateContext: predicateContext,
   251  		predicateResults: predicate.NewResults(),
   252  		start:            tstart,
   253  	}, nil
   254  }
   255  
   256  func (w *worker) commitTransaction(env *environment, tx *txpool.Transaction, coinbase common.Address) ([]*types.Log, error) {
   257  	var (
   258  		snap         = env.state.Snapshot()
   259  		gp           = env.gasPool.Gas()
   260  		blockContext vm.BlockContext
   261  	)
   262  
   263  	if env.rules.IsDurango {
   264  		results, err := core.CheckPredicates(env.rules, env.predicateContext, tx.Tx)
   265  		if err != nil {
   266  			log.Debug("Transaction predicate failed verification in miner", "tx", tx.Tx.Hash(), "err", err)
   267  			return nil, err
   268  		}
   269  		env.predicateResults.SetTxResults(tx.Tx.Hash(), results)
   270  
   271  		blockContext = core.NewEVMBlockContextWithPredicateResults(env.header, w.chain, &coinbase, env.predicateResults)
   272  	} else {
   273  		blockContext = core.NewEVMBlockContext(env.header, w.chain, &coinbase)
   274  	}
   275  
   276  	receipt, err := core.ApplyTransaction(w.chainConfig, w.chain, blockContext, env.gasPool, env.state, env.header, tx.Tx, &env.header.GasUsed, *w.chain.GetVMConfig())
   277  	if err != nil {
   278  		env.state.RevertToSnapshot(snap)
   279  		env.gasPool.SetGas(gp)
   280  		env.predicateResults.DeleteTxResults(tx.Tx.Hash())
   281  		return nil, err
   282  	}
   283  	env.txs = append(env.txs, tx.Tx)
   284  	env.receipts = append(env.receipts, receipt)
   285  	env.size += tx.Tx.Size()
   286  
   287  	return receipt.Logs, nil
   288  }
   289  
   290  func (w *worker) commitTransactions(env *environment, txs *transactionsByPriceAndNonce, coinbase common.Address) {
   291  	for {
   292  		// If we don't have enough gas for any further transactions then we're done.
   293  		if env.gasPool.Gas() < params.TxGas {
   294  			log.Trace("Not enough gas for further transactions", "have", env.gasPool, "want", params.TxGas)
   295  			break
   296  		}
   297  		// Retrieve the next transaction and abort if all done.
   298  		ltx := txs.Peek()
   299  		if ltx == nil {
   300  			break
   301  		}
   302  		tx := ltx.Resolve()
   303  		if tx == nil {
   304  			log.Warn("Ignoring evicted transaction")
   305  
   306  			txs.Pop()
   307  			continue
   308  		}
   309  		// Abort transaction if it won't fit in the block and continue to search for a smaller
   310  		// transction that will fit.
   311  		if totalTxsSize := env.size + tx.Tx.Size(); totalTxsSize > targetTxsSize {
   312  			log.Trace("Skipping transaction that would exceed target size", "hash", tx.Tx.Hash(), "totalTxsSize", totalTxsSize, "txSize", tx.Tx.Size())
   313  
   314  			txs.Pop()
   315  			continue
   316  		}
   317  		// Error may be ignored here. The error has already been checked
   318  		// during transaction acceptance is the transaction pool.
   319  		from, _ := types.Sender(env.signer, tx.Tx)
   320  
   321  		// Check whether the tx is replay protected. If we're not in the EIP155 hf
   322  		// phase, start ignoring the sender until we do.
   323  		if tx.Tx.Protected() && !w.chainConfig.IsEIP155(env.header.Number) {
   324  			log.Trace("Ignoring reply protected transaction", "hash", tx.Tx.Hash(), "eip155", w.chainConfig.EIP155Block)
   325  
   326  			txs.Pop()
   327  			continue
   328  		}
   329  		// Start executing the transaction
   330  		env.state.SetTxContext(tx.Tx.Hash(), env.tcount)
   331  
   332  		_, err := w.commitTransaction(env, tx, coinbase)
   333  		switch {
   334  		case errors.Is(err, core.ErrNonceTooLow):
   335  			// New head notification data race between the transaction pool and miner, shift
   336  			log.Trace("Skipping transaction with low nonce", "sender", from, "nonce", tx.Tx.Nonce())
   337  			txs.Shift()
   338  
   339  		case errors.Is(err, nil):
   340  			env.tcount++
   341  			txs.Shift()
   342  
   343  		default:
   344  			// Transaction is regarded as invalid, drop all consecutive transactions from
   345  			// the same sender because of `nonce-too-high` clause.
   346  			log.Debug("Transaction failed, account skipped", "hash", tx.Tx.Hash(), "err", err)
   347  			txs.Pop()
   348  		}
   349  	}
   350  }
   351  
   352  // commit runs any post-transaction state modifications, assembles the final block
   353  // and commits new work if consensus engine is running.
   354  func (w *worker) commit(env *environment) (*types.Block, error) {
   355  	if env.rules.IsDurango {
   356  		predicateResultsBytes, err := env.predicateResults.Bytes()
   357  		if err != nil {
   358  			return nil, fmt.Errorf("failed to marshal predicate results: %w", err)
   359  		}
   360  		env.header.Extra = append(env.header.Extra, predicateResultsBytes...)
   361  	}
   362  	// Deep copy receipts here to avoid interaction between different tasks.
   363  	receipts := copyReceipts(env.receipts)
   364  	block, err := w.engine.FinalizeAndAssemble(w.chain, env.header, env.parent, env.state, env.txs, nil, receipts)
   365  	if err != nil {
   366  		return nil, err
   367  	}
   368  
   369  	return w.handleResult(env, block, time.Now(), receipts)
   370  }
   371  
   372  func (w *worker) handleResult(env *environment, block *types.Block, createdAt time.Time, unfinishedReceipts []*types.Receipt) (*types.Block, error) {
   373  	// Short circuit when receiving duplicate result caused by resubmitting.
   374  	if w.chain.HasBlock(block.Hash(), block.NumberU64()) {
   375  		return nil, fmt.Errorf("produced duplicate block (Hash: %s, Number %d)", block.Hash(), block.NumberU64())
   376  	}
   377  	// Different block could share same sealhash, deep copy here to prevent write-write conflict.
   378  	var (
   379  		hash     = block.Hash()
   380  		receipts = make([]*types.Receipt, len(unfinishedReceipts))
   381  		logs     []*types.Log
   382  	)
   383  	for i, unfinishedReceipt := range unfinishedReceipts {
   384  		receipt := new(types.Receipt)
   385  		receipts[i] = receipt
   386  		*receipt = *unfinishedReceipt
   387  
   388  		// add block location fields
   389  		receipt.BlockHash = hash
   390  		receipt.BlockNumber = block.Number()
   391  		receipt.TransactionIndex = uint(i)
   392  
   393  		// Update the block hash in all logs since it is now available and not when the
   394  		// receipt/log of individual transactions were created.
   395  		receipt.Logs = make([]*types.Log, len(unfinishedReceipt.Logs))
   396  		for j, unfinishedLog := range unfinishedReceipt.Logs {
   397  			log := new(types.Log)
   398  			receipt.Logs[j] = log
   399  			*log = *unfinishedLog
   400  			log.BlockHash = hash
   401  		}
   402  		logs = append(logs, receipt.Logs...)
   403  	}
   404  
   405  	feesInEther, err := core.TotalFeesFloat(block, receipts)
   406  	if err != nil {
   407  		log.Error("TotalFeesFloat error: %s", err)
   408  	}
   409  	log.Info("Commit new mining work", "number", block.Number(), "hash", hash,
   410  		"uncles", 0, "txs", env.tcount,
   411  		"gas", block.GasUsed(), "fees", feesInEther,
   412  		"elapsed", common.PrettyDuration(time.Since(env.start)))
   413  
   414  	// Note: the miner no longer emits a NewMinedBlock event. Instead the caller
   415  	// is responsible for running any additional verification and then inserting
   416  	// the block with InsertChain, which will also emit a new head event.
   417  	return block, nil
   418  }
   419  
   420  // copyReceipts makes a deep copy of the given receipts.
   421  func copyReceipts(receipts []*types.Receipt) []*types.Receipt {
   422  	result := make([]*types.Receipt, len(receipts))
   423  	for i, l := range receipts {
   424  		cpy := *l
   425  		result[i] = &cpy
   426  	}
   427  	return result
   428  }