github.com/MetalBlockchain/subnet-evm@v0.4.9/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/types"
    46  	"github.com/MetalBlockchain/subnet-evm/params"
    47  	"github.com/ethereum/go-ethereum/common"
    48  	"github.com/ethereum/go-ethereum/event"
    49  	"github.com/ethereum/go-ethereum/log"
    50  )
    51  
    52  const (
    53  	targetTxsSize = 1800 * units.KiB
    54  )
    55  
    56  // environment is the worker's current environment and holds all of the current state information.
    57  type environment struct {
    58  	signer types.Signer
    59  
    60  	state   *state.StateDB // apply state changes here
    61  	tcount  int            // tx count in cycle
    62  	gasPool *core.GasPool  // available gas used to pack transactions
    63  
    64  	parent   *types.Header
    65  	header   *types.Header
    66  	txs      []*types.Transaction
    67  	receipts []*types.Receipt
    68  	size     common.StorageSize
    69  
    70  	start time.Time // Time that block building began
    71  }
    72  
    73  // worker is the main object which takes care of submitting new work to consensus engine
    74  // and gathering the sealing result.
    75  type worker struct {
    76  	config      *Config
    77  	chainConfig *params.ChainConfig
    78  	engine      consensus.Engine
    79  	eth         Backend
    80  	chain       *core.BlockChain
    81  
    82  	// Feeds
    83  	// TODO remove since this will never be written to
    84  	pendingLogsFeed event.Feed
    85  
    86  	// Subscriptions
    87  	mux      *event.TypeMux // TODO replace
    88  	mu       sync.RWMutex   // The lock used to protect the coinbase and extra fields
    89  	coinbase common.Address
    90  	clock    *mockable.Clock // Allows us mock the clock for testing
    91  }
    92  
    93  func newWorker(config *Config, chainConfig *params.ChainConfig, engine consensus.Engine, eth Backend, mux *event.TypeMux, clock *mockable.Clock) *worker {
    94  	worker := &worker{
    95  		config:      config,
    96  		chainConfig: chainConfig,
    97  		engine:      engine,
    98  		eth:         eth,
    99  		mux:         mux,
   100  		chain:       eth.BlockChain(),
   101  		clock:       clock,
   102  	}
   103  
   104  	return worker
   105  }
   106  
   107  // setEtherbase sets the etherbase used to initialize the block coinbase field.
   108  func (w *worker) setEtherbase(addr common.Address) {
   109  	w.mu.Lock()
   110  	defer w.mu.Unlock()
   111  	w.coinbase = addr
   112  }
   113  
   114  // commitNewWork generates several new sealing tasks based on the parent block.
   115  func (w *worker) commitNewWork() (*types.Block, error) {
   116  	w.mu.RLock()
   117  	defer w.mu.RUnlock()
   118  
   119  	tstart := w.clock.Time()
   120  	timestamp := uint64(tstart.Unix())
   121  	parent := w.chain.CurrentBlock()
   122  	// Note: in order to support asynchronous block production, blocks are allowed to have
   123  	// the same timestamp as their parent. This allows more than one block to be produced
   124  	// per second.
   125  	if parent.Time() >= timestamp {
   126  		timestamp = parent.Time()
   127  	}
   128  
   129  	bigTimestamp := new(big.Int).SetUint64(timestamp)
   130  	var gasLimit uint64
   131  	// The fee config manager relies on the state of the parent block to set the fee config
   132  	// because the fee config may be changed by the current block.
   133  	feeConfig, _, err := w.chain.GetFeeConfigAt(parent.Header())
   134  	if err != nil {
   135  		return nil, err
   136  	}
   137  	configuredGasLimit := feeConfig.GasLimit.Uint64()
   138  	if w.chainConfig.IsSubnetEVM(bigTimestamp) {
   139  		gasLimit = configuredGasLimit
   140  	} else {
   141  		// The gas limit is set in SubnetEVMGasLimit because the ceiling and floor were set to the same value
   142  		// such that the gas limit converged to it. Since this is hardbaked now, we remove the ability to configure it.
   143  		gasLimit = core.CalcGasLimit(parent.GasUsed(), parent.GasLimit(), configuredGasLimit, configuredGasLimit)
   144  	}
   145  	num := parent.Number()
   146  	header := &types.Header{
   147  		ParentHash: parent.Hash(),
   148  		Number:     num.Add(num, common.Big1),
   149  		GasLimit:   gasLimit,
   150  		Extra:      nil,
   151  		Time:       timestamp,
   152  	}
   153  
   154  	if w.chainConfig.IsSubnetEVM(bigTimestamp) {
   155  		var err error
   156  		header.Extra, header.BaseFee, err = dummy.CalcBaseFee(w.chainConfig, feeConfig, parent.Header(), timestamp)
   157  		if err != nil {
   158  			return nil, fmt.Errorf("failed to calculate new base fee: %w", err)
   159  		}
   160  	}
   161  
   162  	if w.coinbase == (common.Address{}) {
   163  		return nil, errors.New("cannot mine without etherbase")
   164  	}
   165  	header.Coinbase = w.coinbase
   166  
   167  	configuredCoinbase, isAllowFeeRecipient, err := w.chain.GetCoinbaseAt(parent.Header())
   168  	if err != nil {
   169  		return nil, fmt.Errorf("failed to get configured coinbase: %w", err)
   170  	}
   171  
   172  	// if fee recipients are not allowed, then the coinbase is the configured coinbase
   173  	// don't set w.coinbase directly to the configured coinbase because that would override the
   174  	// coinbase set by the user
   175  	if !isAllowFeeRecipient && w.coinbase != configuredCoinbase {
   176  		log.Info("fee recipients are not allowed, using required coinbase for the mining", "currentminer", w.coinbase, "required", configuredCoinbase)
   177  		header.Coinbase = configuredCoinbase
   178  	}
   179  
   180  	if err := w.engine.Prepare(w.chain, header); err != nil {
   181  		return nil, fmt.Errorf("failed to prepare header for mining: %w", err)
   182  	}
   183  
   184  	env, err := w.createCurrentEnvironment(parent, header, tstart)
   185  	if err != nil {
   186  		return nil, fmt.Errorf("failed to create new current environment: %w", err)
   187  	}
   188  	// Configure any stateful precompiles that should go into effect during this block.
   189  	w.chainConfig.CheckConfigurePrecompiles(new(big.Int).SetUint64(parent.Time()), types.NewBlockWithHeader(header), env.state)
   190  
   191  	// Fill the block with all available pending transactions.
   192  	pending := w.eth.TxPool().Pending(true)
   193  
   194  	// Split the pending transactions into locals and remotes
   195  	localTxs := make(map[common.Address]types.Transactions)
   196  	remoteTxs := pending
   197  	for _, account := range w.eth.TxPool().Locals() {
   198  		if txs := remoteTxs[account]; len(txs) > 0 {
   199  			delete(remoteTxs, account)
   200  			localTxs[account] = txs
   201  		}
   202  	}
   203  	if len(localTxs) > 0 {
   204  		txs := types.NewTransactionsByPriceAndNonce(env.signer, localTxs, header.BaseFee)
   205  		w.commitTransactions(env, txs, header.Coinbase)
   206  	}
   207  	if len(remoteTxs) > 0 {
   208  		txs := types.NewTransactionsByPriceAndNonce(env.signer, remoteTxs, header.BaseFee)
   209  		w.commitTransactions(env, txs, header.Coinbase)
   210  	}
   211  
   212  	return w.commit(env)
   213  }
   214  
   215  func (w *worker) createCurrentEnvironment(parent *types.Block, header *types.Header, tstart time.Time) (*environment, error) {
   216  	state, err := w.chain.StateAt(parent.Root())
   217  	if err != nil {
   218  		return nil, err
   219  	}
   220  	return &environment{
   221  		signer:  types.MakeSigner(w.chainConfig, header.Number, new(big.Int).SetUint64(header.Time)),
   222  		state:   state,
   223  		parent:  parent.Header(),
   224  		header:  header,
   225  		tcount:  0,
   226  		gasPool: new(core.GasPool).AddGas(header.GasLimit),
   227  		start:   tstart,
   228  	}, nil
   229  }
   230  
   231  func (w *worker) commitTransaction(env *environment, tx *types.Transaction, coinbase common.Address) ([]*types.Log, error) {
   232  	snap := env.state.Snapshot()
   233  
   234  	receipt, err := core.ApplyTransaction(w.chainConfig, w.chain, &coinbase, env.gasPool, env.state, env.header, tx, &env.header.GasUsed, *w.chain.GetVMConfig())
   235  	if err != nil {
   236  		env.state.RevertToSnapshot(snap)
   237  		return nil, err
   238  	}
   239  	env.txs = append(env.txs, tx)
   240  	env.receipts = append(env.receipts, receipt)
   241  	env.size += tx.Size()
   242  
   243  	return receipt.Logs, nil
   244  }
   245  
   246  func (w *worker) commitTransactions(env *environment, txs *types.TransactionsByPriceAndNonce, coinbase common.Address) {
   247  	for {
   248  		// If we don't have enough gas for any further transactions then we're done
   249  		if env.gasPool.Gas() < params.TxGas {
   250  			log.Trace("Not enough gas for further transactions", "have", env.gasPool, "want", params.TxGas)
   251  			break
   252  		}
   253  		// Retrieve the next transaction and abort if all done
   254  		tx := txs.Peek()
   255  		if tx == nil {
   256  			break
   257  		}
   258  		// Abort transaction if it won't fit in the block and continue to search for a smaller
   259  		// transction that will fit.
   260  		if totalTxsSize := env.size + tx.Size(); totalTxsSize > targetTxsSize {
   261  			log.Trace("Skipping transaction that would exceed target size", "hash", tx.Hash(), "totalTxsSize", totalTxsSize, "txSize", tx.Size())
   262  
   263  			txs.Pop()
   264  			continue
   265  		}
   266  		// Error may be ignored here. The error has already been checked
   267  		// during transaction acceptance is the transaction pool.
   268  		//
   269  		// We use the eip155 signer regardless of the current hf.
   270  		from, _ := types.Sender(env.signer, tx)
   271  		// Check whether the tx is replay protected. If we're not in the EIP155 hf
   272  		// phase, start ignoring the sender until we do.
   273  		if tx.Protected() && !w.chainConfig.IsEIP155(env.header.Number) {
   274  			log.Trace("Ignoring reply protected transaction", "hash", tx.Hash(), "eip155", w.chainConfig.EIP155Block)
   275  
   276  			txs.Pop()
   277  			continue
   278  		}
   279  		// Start executing the transaction
   280  		env.state.Prepare(tx.Hash(), env.tcount)
   281  
   282  		_, err := w.commitTransaction(env, tx, coinbase)
   283  		switch {
   284  		case errors.Is(err, core.ErrGasLimitReached):
   285  			// Pop the current out-of-gas transaction without shifting in the next from the account
   286  			log.Trace("Gas limit exceeded for current block", "sender", from)
   287  			txs.Pop()
   288  
   289  		case errors.Is(err, core.ErrNonceTooLow):
   290  			// New head notification data race between the transaction pool and miner, shift
   291  			log.Trace("Skipping transaction with low nonce", "sender", from, "nonce", tx.Nonce())
   292  			txs.Shift()
   293  
   294  		case errors.Is(err, core.ErrNonceTooHigh):
   295  			// Reorg notification data race between the transaction pool and miner, skip account =
   296  			log.Trace("Skipping account with high nonce", "sender", from, "nonce", tx.Nonce())
   297  			txs.Pop()
   298  
   299  		case errors.Is(err, nil):
   300  			env.tcount++
   301  			txs.Shift()
   302  
   303  		case errors.Is(err, core.ErrTxTypeNotSupported):
   304  			// Pop the unsupported transaction without shifting in the next from the account
   305  			log.Trace("Skipping unsupported transaction type", "sender", from, "type", tx.Type())
   306  			txs.Pop()
   307  
   308  		default:
   309  			// Strange error, discard the transaction and get the next in line (note, the
   310  			// nonce-too-high clause will prevent us from executing in vain).
   311  			log.Debug("Transaction failed, account skipped", "hash", tx.Hash(), "err", err)
   312  			txs.Shift()
   313  		}
   314  	}
   315  }
   316  
   317  // commit runs any post-transaction state modifications, assembles the final block
   318  // and commits new work if consensus engine is running.
   319  func (w *worker) commit(env *environment) (*types.Block, error) {
   320  	// Deep copy receipts here to avoid interaction between different tasks.
   321  	receipts := copyReceipts(env.receipts)
   322  	block, err := w.engine.FinalizeAndAssemble(w.chain, env.header, env.parent, env.state, env.txs, nil, receipts)
   323  	if err != nil {
   324  		return nil, err
   325  	}
   326  
   327  	return w.handleResult(env, block, time.Now(), receipts)
   328  }
   329  
   330  func (w *worker) handleResult(env *environment, block *types.Block, createdAt time.Time, unfinishedReceipts []*types.Receipt) (*types.Block, error) {
   331  	// Short circuit when receiving duplicate result caused by resubmitting.
   332  	if w.chain.HasBlock(block.Hash(), block.NumberU64()) {
   333  		return nil, fmt.Errorf("produced duplicate block (Hash: %s, Number %d)", block.Hash(), block.NumberU64())
   334  	}
   335  	// Different block could share same sealhash, deep copy here to prevent write-write conflict.
   336  	var (
   337  		hash     = block.Hash()
   338  		receipts = make([]*types.Receipt, len(unfinishedReceipts))
   339  		logs     []*types.Log
   340  	)
   341  	for i, unfinishedReceipt := range unfinishedReceipts {
   342  		receipt := new(types.Receipt)
   343  		receipts[i] = receipt
   344  		*receipt = *unfinishedReceipt
   345  
   346  		// add block location fields
   347  		receipt.BlockHash = hash
   348  		receipt.BlockNumber = block.Number()
   349  		receipt.TransactionIndex = uint(i)
   350  
   351  		// Update the block hash in all logs since it is now available and not when the
   352  		// receipt/log of individual transactions were created.
   353  		receipt.Logs = make([]*types.Log, len(unfinishedReceipt.Logs))
   354  		for j, unfinishedLog := range unfinishedReceipt.Logs {
   355  			log := new(types.Log)
   356  			receipt.Logs[j] = log
   357  			*log = *unfinishedLog
   358  			log.BlockHash = hash
   359  		}
   360  		logs = append(logs, receipt.Logs...)
   361  	}
   362  
   363  	log.Info("Commit new mining work", "number", block.Number(), "hash", hash, "uncles", 0, "txs", env.tcount,
   364  		"gas", block.GasUsed(), "fees", totalFees(block, receipts), "elapsed", common.PrettyDuration(time.Since(env.start)))
   365  
   366  	// Note: the miner no longer emits a NewMinedBlock event. Instead the caller
   367  	// is responsible for running any additional verification and then inserting
   368  	// the block with InsertChain, which will also emit a new head event.
   369  	return block, nil
   370  }
   371  
   372  // copyReceipts makes a deep copy of the given receipts.
   373  func copyReceipts(receipts []*types.Receipt) []*types.Receipt {
   374  	result := make([]*types.Receipt, len(receipts))
   375  	for i, l := range receipts {
   376  		cpy := *l
   377  		result[i] = &cpy
   378  	}
   379  	return result
   380  }
   381  
   382  // totalFees computes total consumed fees in ETH. Block transactions and receipts have to have the same order.
   383  func totalFees(block *types.Block, receipts []*types.Receipt) *big.Float {
   384  	feesWei := new(big.Int)
   385  	for i, tx := range block.Transactions() {
   386  		feesWei.Add(feesWei, new(big.Int).Mul(new(big.Int).SetUint64(receipts[i].GasUsed), tx.GasPrice()))
   387  	}
   388  	return new(big.Float).Quo(new(big.Float).SetInt(feesWei), new(big.Float).SetInt(big.NewInt(params.Ether)))
   389  }