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