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