github.com/dim4egster/coreth@v0.10.2/consensus/dummy/consensus.go (about)

     1  // (c) 2019-2020, Ava Labs, Inc. All rights reserved.
     2  // See the file LICENSE for licensing terms.
     3  
     4  package dummy
     5  
     6  import (
     7  	"bytes"
     8  	"errors"
     9  	"fmt"
    10  	"math/big"
    11  	"time"
    12  
    13  	"github.com/dim4egster/coreth/consensus"
    14  	"github.com/dim4egster/coreth/core/state"
    15  	"github.com/dim4egster/coreth/core/types"
    16  	"github.com/dim4egster/coreth/params"
    17  	"github.com/dim4egster/coreth/rpc"
    18  	"github.com/dim4egster/coreth/trie"
    19  	"github.com/ethereum/go-ethereum/common"
    20  )
    21  
    22  var (
    23  	allowedFutureBlockTime = 10 * time.Second // Max time from current time allowed for blocks, before they're considered future blocks
    24  
    25  	errInvalidBlockTime       = errors.New("timestamp less than parent's")
    26  	errUnclesUnsupported      = errors.New("uncles unsupported")
    27  	errBlockGasCostNil        = errors.New("block gas cost is nil")
    28  	errBlockGasCostTooLarge   = errors.New("block gas cost is not uint64")
    29  	errBaseFeeNil             = errors.New("base fee is nil")
    30  	errExtDataGasUsedNil      = errors.New("extDataGasUsed is nil")
    31  	errExtDataGasUsedTooLarge = errors.New("extDataGasUsed is not uint64")
    32  )
    33  
    34  type Mode uint
    35  
    36  const (
    37  	ModeSkipHeader   Mode = 1 // Skip over header verification
    38  	ModeSkipBlockFee Mode = 2 // Skip block fee verification
    39  )
    40  
    41  type (
    42  	OnFinalizeAndAssembleCallbackType = func(header *types.Header, state *state.StateDB, txs []*types.Transaction) (extraData []byte, blockFeeContribution *big.Int, extDataGasUsed *big.Int, err error)
    43  	OnAPIsCallbackType                = func(consensus.ChainHeaderReader) []rpc.API
    44  	OnExtraStateChangeType            = func(block *types.Block, statedb *state.StateDB) (blockFeeContribution *big.Int, extDataGasUsed *big.Int, err error)
    45  
    46  	ConsensusCallbacks struct {
    47  		OnFinalizeAndAssemble OnFinalizeAndAssembleCallbackType
    48  		OnExtraStateChange    OnExtraStateChangeType
    49  	}
    50  
    51  	DummyEngine struct {
    52  		cb            *ConsensusCallbacks
    53  		consensusMode Mode
    54  	}
    55  )
    56  
    57  func NewDummyEngine(cb *ConsensusCallbacks) *DummyEngine {
    58  	return &DummyEngine{
    59  		cb: cb,
    60  	}
    61  }
    62  
    63  func NewETHFaker() *DummyEngine {
    64  	return &DummyEngine{
    65  		cb:            new(ConsensusCallbacks),
    66  		consensusMode: ModeSkipBlockFee,
    67  	}
    68  }
    69  
    70  func NewComplexETHFaker(cb *ConsensusCallbacks) *DummyEngine {
    71  	return &DummyEngine{
    72  		cb:            cb,
    73  		consensusMode: ModeSkipBlockFee,
    74  	}
    75  }
    76  
    77  func NewFaker() *DummyEngine {
    78  	return NewDummyEngine(new(ConsensusCallbacks))
    79  }
    80  
    81  func NewFullFaker() *DummyEngine {
    82  	return &DummyEngine{
    83  		cb:            new(ConsensusCallbacks),
    84  		consensusMode: ModeSkipHeader,
    85  	}
    86  }
    87  
    88  func (self *DummyEngine) verifyHeaderGasFields(config *params.ChainConfig, header *types.Header, parent *types.Header) error {
    89  	timestamp := new(big.Int).SetUint64(header.Time)
    90  
    91  	// Verify that the gas limit is <= 2^63-1
    92  	if header.GasLimit > params.MaxGasLimit {
    93  		return fmt.Errorf("invalid gasLimit: have %v, max %v", header.GasLimit, params.MaxGasLimit)
    94  	}
    95  	// Verify that the gasUsed is <= gasLimit
    96  	if header.GasUsed > header.GasLimit {
    97  		return fmt.Errorf("invalid gasUsed: have %d, gasLimit %d", header.GasUsed, header.GasLimit)
    98  	}
    99  	if config.IsApricotPhase1(timestamp) {
   100  		if header.GasLimit != params.ApricotPhase1GasLimit {
   101  			return fmt.Errorf("expected gas limit to be %d, but found %d", params.ApricotPhase1GasLimit, header.GasLimit)
   102  		}
   103  	} else {
   104  		// Verify that the gas limit remains within allowed bounds
   105  		diff := int64(parent.GasLimit) - int64(header.GasLimit)
   106  		if diff < 0 {
   107  			diff *= -1
   108  		}
   109  		limit := parent.GasLimit / params.GasLimitBoundDivisor
   110  
   111  		if uint64(diff) >= limit || header.GasLimit < params.MinGasLimit {
   112  			return fmt.Errorf("invalid gas limit: have %d, want %d += %d", header.GasLimit, parent.GasLimit, limit)
   113  		}
   114  	}
   115  
   116  	if !config.IsApricotPhase3(timestamp) {
   117  		// Verify BaseFee is not present before AP3
   118  		if header.BaseFee != nil {
   119  			return fmt.Errorf("invalid baseFee before fork: have %d, want <nil>", header.BaseFee)
   120  		}
   121  	} else {
   122  		// Verify baseFee and rollupWindow encoding as part of header verification
   123  		// starting in AP3
   124  		expectedRollupWindowBytes, expectedBaseFee, err := CalcBaseFee(config, parent, header.Time)
   125  		if err != nil {
   126  			return fmt.Errorf("failed to calculate base fee: %w", err)
   127  		}
   128  		if !bytes.Equal(expectedRollupWindowBytes, header.Extra) {
   129  			return fmt.Errorf("expected rollup window bytes: %x, found %x", expectedRollupWindowBytes, header.Extra)
   130  		}
   131  		if header.BaseFee == nil {
   132  			return errors.New("expected baseFee to be non-nil")
   133  		}
   134  		if bfLen := header.BaseFee.BitLen(); bfLen > 256 {
   135  			return fmt.Errorf("too large base fee: bitlen %d", bfLen)
   136  		}
   137  		if header.BaseFee.Cmp(expectedBaseFee) != 0 {
   138  			return fmt.Errorf("expected base fee (%d), found (%d)", expectedBaseFee, header.BaseFee)
   139  		}
   140  	}
   141  
   142  	// Verify BlockGasCost, ExtDataGasUsed not present before AP4
   143  	if !config.IsApricotPhase4(timestamp) {
   144  		if header.BlockGasCost != nil {
   145  			return fmt.Errorf("invalid blockGasCost before fork: have %d, want <nil>", header.BlockGasCost)
   146  		}
   147  		if header.ExtDataGasUsed != nil {
   148  			return fmt.Errorf("invalid extDataGasUsed before fork: have %d, want <nil>", header.ExtDataGasUsed)
   149  		}
   150  		return nil
   151  	}
   152  
   153  	// Enforce BlockGasCost constraints
   154  	blockGasCostStep := ApricotPhase4BlockGasCostStep
   155  	if config.IsApricotPhase5(timestamp) {
   156  		blockGasCostStep = ApricotPhase5BlockGasCostStep
   157  	}
   158  	expectedBlockGasCost := calcBlockGasCost(
   159  		ApricotPhase4TargetBlockRate,
   160  		ApricotPhase4MinBlockGasCost,
   161  		ApricotPhase4MaxBlockGasCost,
   162  		blockGasCostStep,
   163  		parent.BlockGasCost,
   164  		parent.Time, header.Time,
   165  	)
   166  	if header.BlockGasCost == nil {
   167  		return errBlockGasCostNil
   168  	}
   169  	if !header.BlockGasCost.IsUint64() {
   170  		return errBlockGasCostTooLarge
   171  	}
   172  	if header.BlockGasCost.Cmp(expectedBlockGasCost) != 0 {
   173  		return fmt.Errorf("invalid block gas cost: have %d, want %d", header.BlockGasCost, expectedBlockGasCost)
   174  	}
   175  	// ExtDataGasUsed correctness is checked during block validation
   176  	// (when the validator has access to the block contents)
   177  	if header.ExtDataGasUsed == nil {
   178  		return errExtDataGasUsedNil
   179  	}
   180  	if !header.ExtDataGasUsed.IsUint64() {
   181  		return errExtDataGasUsedTooLarge
   182  	}
   183  	return nil
   184  }
   185  
   186  // modified from consensus.go
   187  func (self *DummyEngine) verifyHeader(chain consensus.ChainHeaderReader, header *types.Header, parent *types.Header, uncle bool) error {
   188  	var (
   189  		config    = chain.Config()
   190  		timestamp = new(big.Int).SetUint64(header.Time)
   191  	)
   192  	// Ensure that we do not verify an uncle
   193  	if uncle {
   194  		return errUnclesUnsupported
   195  	}
   196  	// Ensure that the header's extra-data section is of a reasonable size
   197  	if !config.IsApricotPhase3(timestamp) {
   198  		if uint64(len(header.Extra)) > params.MaximumExtraDataSize {
   199  			return fmt.Errorf("extra-data too long: %d > %d", len(header.Extra), params.MaximumExtraDataSize)
   200  		}
   201  	} else {
   202  		if uint64(len(header.Extra)) != params.ApricotPhase3ExtraDataSize {
   203  			return fmt.Errorf("expected extra-data field to be: %d, but found %d", params.ApricotPhase3ExtraDataSize, len(header.Extra))
   204  		}
   205  	}
   206  	// Ensure gas-related header fields are correct
   207  	if err := self.verifyHeaderGasFields(config, header, parent); err != nil {
   208  		return err
   209  	}
   210  	// Verify the header's timestamp
   211  	if header.Time > uint64(time.Now().Add(allowedFutureBlockTime).Unix()) {
   212  		return consensus.ErrFutureBlock
   213  	}
   214  	//if header.Time <= parent.Time {
   215  	if header.Time < parent.Time {
   216  		return errInvalidBlockTime
   217  	}
   218  	// Verify that the block number is parent's +1
   219  	if diff := new(big.Int).Sub(header.Number, parent.Number); diff.Cmp(big.NewInt(1)) != 0 {
   220  		return consensus.ErrInvalidNumber
   221  	}
   222  	return nil
   223  }
   224  
   225  func (self *DummyEngine) Author(header *types.Header) (common.Address, error) {
   226  	return header.Coinbase, nil
   227  }
   228  
   229  func (self *DummyEngine) VerifyHeader(chain consensus.ChainHeaderReader, header *types.Header) error {
   230  	// If we're running a full engine faking, accept any input as valid
   231  	if self.consensusMode == ModeSkipHeader {
   232  		return nil
   233  	}
   234  	// Short circuit if the header is known, or it's parent not
   235  	number := header.Number.Uint64()
   236  	if chain.GetHeader(header.Hash(), number) != nil {
   237  		return nil
   238  	}
   239  	parent := chain.GetHeader(header.ParentHash, number-1)
   240  	if parent == nil {
   241  		return consensus.ErrUnknownAncestor
   242  	}
   243  	// Sanity checks passed, do a proper verification
   244  	return self.verifyHeader(chain, header, parent, false)
   245  }
   246  
   247  func (self *DummyEngine) VerifyUncles(chain consensus.ChainReader, block *types.Block) error {
   248  	if len(block.Uncles()) > 0 {
   249  		return errUnclesUnsupported
   250  	}
   251  	return nil
   252  }
   253  
   254  func (self *DummyEngine) Prepare(chain consensus.ChainHeaderReader, header *types.Header) error {
   255  	header.Difficulty = big.NewInt(1)
   256  	return nil
   257  }
   258  
   259  func (self *DummyEngine) verifyBlockFee(
   260  	baseFee *big.Int,
   261  	requiredBlockGasCost *big.Int,
   262  	txs []*types.Transaction,
   263  	receipts []*types.Receipt,
   264  	extraStateChangeContribution *big.Int,
   265  ) error {
   266  	if self.consensusMode == ModeSkipBlockFee {
   267  		return nil
   268  	}
   269  	if baseFee == nil || baseFee.Sign() <= 0 {
   270  		return fmt.Errorf("invalid base fee (%d) in apricot phase 4", baseFee)
   271  	}
   272  	if requiredBlockGasCost == nil || !requiredBlockGasCost.IsUint64() {
   273  		return fmt.Errorf("invalid block gas cost (%d) in apricot phase 4", requiredBlockGasCost)
   274  	}
   275  
   276  	var (
   277  		gasUsed              = new(big.Int)
   278  		blockFeeContribution = new(big.Int)
   279  		totalBlockFee        = new(big.Int)
   280  	)
   281  
   282  	// Add in the external contribution
   283  	if extraStateChangeContribution != nil {
   284  		if extraStateChangeContribution.Cmp(common.Big0) < 0 {
   285  			return fmt.Errorf("invalid extra state change contribution: %d", extraStateChangeContribution)
   286  		}
   287  		totalBlockFee.Add(totalBlockFee, extraStateChangeContribution)
   288  	}
   289  
   290  	// Calculate the total excess (denominated in AVAX) over the base fee that was paid towards the block fee
   291  	for i, receipt := range receipts {
   292  		// Each transaction contributes the excess over the baseFee towards the totalBlockFee
   293  		// This should be equivalent to the sum of the "priority fees" within EIP-1559.
   294  		txFeePremium, err := txs[i].EffectiveGasTip(baseFee)
   295  		if err != nil {
   296  			return err
   297  		}
   298  		// Multiply the [txFeePremium] by the gasUsed in the transaction since this gives the total AVAX that was paid
   299  		// above the amount required if the transaction had simply paid the minimum base fee for the block.
   300  		//
   301  		// Ex. LegacyTx paying a gas price of 100 gwei for 1M gas in a block with a base fee of 10 gwei.
   302  		// Total Fee = 100 gwei * 1M gas
   303  		// Minimum Fee = 10 gwei * 1M gas (minimum fee that would have been accepted for this transaction)
   304  		// Fee Premium = 90 gwei
   305  		// Total Overpaid = 90 gwei * 1M gas
   306  		blockFeeContribution.Mul(txFeePremium, gasUsed.SetUint64(receipt.GasUsed))
   307  		totalBlockFee.Add(totalBlockFee, blockFeeContribution)
   308  	}
   309  	// Calculate how much gas the [totalBlockFee] would purchase at the price level
   310  	// set by the base fee of this block.
   311  	blockGas := new(big.Int).Div(totalBlockFee, baseFee)
   312  
   313  	// Require that the amount of gas purchased by the effective tips within the block, [blockGas],
   314  	// covers at least [requiredBlockGasCost].
   315  	//
   316  	// NOTE: To determine the [requiredBlockFee], multiply [requiredBlockGasCost]
   317  	// by [baseFee].
   318  	if blockGas.Cmp(requiredBlockGasCost) < 0 {
   319  		return fmt.Errorf(
   320  			"insufficient gas (%d) to cover the block cost (%d) at base fee (%d) (total block fee: %d)",
   321  			blockGas, requiredBlockGasCost, baseFee, totalBlockFee,
   322  		)
   323  	}
   324  	return nil
   325  }
   326  
   327  func (self *DummyEngine) Finalize(chain consensus.ChainHeaderReader, block *types.Block, parent *types.Header, state *state.StateDB, receipts []*types.Receipt) error {
   328  	// Perform extra state change while finalizing the block
   329  	var (
   330  		contribution, extDataGasUsed *big.Int
   331  		err                          error
   332  	)
   333  	if self.cb.OnExtraStateChange != nil {
   334  		contribution, extDataGasUsed, err = self.cb.OnExtraStateChange(block, state)
   335  		if err != nil {
   336  			return err
   337  		}
   338  	}
   339  	if chain.Config().IsApricotPhase4(new(big.Int).SetUint64(block.Time())) {
   340  		// Validate extDataGasUsed and BlockGasCost match expectations
   341  		//
   342  		// NOTE: This is a duplicate check of what is already performed in
   343  		// blockValidator but is done here for symmetry with FinalizeAndAssemble.
   344  		if extDataGasUsed == nil {
   345  			extDataGasUsed = new(big.Int).Set(common.Big0)
   346  		}
   347  		if blockExtDataGasUsed := block.ExtDataGasUsed(); blockExtDataGasUsed == nil || !blockExtDataGasUsed.IsUint64() || blockExtDataGasUsed.Cmp(extDataGasUsed) != 0 {
   348  			return fmt.Errorf("invalid extDataGasUsed: have %d, want %d", blockExtDataGasUsed, extDataGasUsed)
   349  		}
   350  		blockGasCostStep := ApricotPhase4BlockGasCostStep
   351  		if chain.Config().IsApricotPhase5(new(big.Int).SetUint64(block.Time())) {
   352  			blockGasCostStep = ApricotPhase5BlockGasCostStep
   353  		}
   354  		// Calculate the expected blockGasCost for this block.
   355  		// Note: this is a deterministic transtion that defines an exact block fee for this block.
   356  		blockGasCost := calcBlockGasCost(
   357  			ApricotPhase4TargetBlockRate,
   358  			ApricotPhase4MinBlockGasCost,
   359  			ApricotPhase4MaxBlockGasCost,
   360  			blockGasCostStep,
   361  			parent.BlockGasCost,
   362  			parent.Time, block.Time(),
   363  		)
   364  		// Verify the BlockGasCost set in the header matches the calculated value.
   365  		if blockBlockGasCost := block.BlockGasCost(); blockBlockGasCost == nil || !blockBlockGasCost.IsUint64() || blockBlockGasCost.Cmp(blockGasCost) != 0 {
   366  			return fmt.Errorf("invalid blockGasCost: have %d, want %d", blockBlockGasCost, blockGasCost)
   367  		}
   368  		// Verify the block fee was paid.
   369  		if err := self.verifyBlockFee(
   370  			block.BaseFee(),
   371  			block.BlockGasCost(),
   372  			block.Transactions(),
   373  			receipts,
   374  			contribution,
   375  		); err != nil {
   376  			return err
   377  		}
   378  	}
   379  
   380  	return nil
   381  }
   382  
   383  func (self *DummyEngine) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, parent *types.Header, state *state.StateDB, txs []*types.Transaction,
   384  	uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) {
   385  	var (
   386  		contribution, extDataGasUsed *big.Int
   387  		extraData                    []byte
   388  		err                          error
   389  	)
   390  	if self.cb.OnFinalizeAndAssemble != nil {
   391  		extraData, contribution, extDataGasUsed, err = self.cb.OnFinalizeAndAssemble(header, state, txs)
   392  		if err != nil {
   393  			return nil, err
   394  		}
   395  	}
   396  	if chain.Config().IsApricotPhase4(new(big.Int).SetUint64(header.Time)) {
   397  		header.ExtDataGasUsed = extDataGasUsed
   398  		if header.ExtDataGasUsed == nil {
   399  			header.ExtDataGasUsed = new(big.Int).Set(common.Big0)
   400  		}
   401  		blockGasCostStep := ApricotPhase4BlockGasCostStep
   402  		if chain.Config().IsApricotPhase5(new(big.Int).SetUint64(header.Time)) {
   403  			blockGasCostStep = ApricotPhase5BlockGasCostStep
   404  		}
   405  		// Calculate the required block gas cost for this block.
   406  		header.BlockGasCost = calcBlockGasCost(
   407  			ApricotPhase4TargetBlockRate,
   408  			ApricotPhase4MinBlockGasCost,
   409  			ApricotPhase4MaxBlockGasCost,
   410  			blockGasCostStep,
   411  			parent.BlockGasCost,
   412  			parent.Time, header.Time,
   413  		)
   414  		// Verify that this block covers the block fee.
   415  		if err := self.verifyBlockFee(
   416  			header.BaseFee,
   417  			header.BlockGasCost,
   418  			txs,
   419  			receipts,
   420  			contribution,
   421  		); err != nil {
   422  			return nil, err
   423  		}
   424  	}
   425  	// commit the final state root
   426  	header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number))
   427  
   428  	// Header seems complete, assemble into a block and return
   429  	return types.NewBlock(
   430  		header, txs, uncles, receipts, new(trie.Trie), extraData,
   431  		chain.Config().IsApricotPhase1(new(big.Int).SetUint64(header.Time)),
   432  	), nil
   433  }
   434  
   435  func (self *DummyEngine) CalcDifficulty(chain consensus.ChainHeaderReader, time uint64, parent *types.Header) *big.Int {
   436  	return big.NewInt(1)
   437  }
   438  
   439  func (self *DummyEngine) Close() error {
   440  	return nil
   441  }