github.com/cgcardona/r-subnet-evm@v0.1.5/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/ava-labs/avalanchego/utils/timer/mockable"
    14  	"github.com/cgcardona/r-subnet-evm/consensus"
    15  	"github.com/cgcardona/r-subnet-evm/core/state"
    16  	"github.com/cgcardona/r-subnet-evm/core/types"
    17  	"github.com/cgcardona/r-subnet-evm/params"
    18  	"github.com/cgcardona/r-subnet-evm/trie"
    19  	"github.com/cgcardona/r-subnet-evm/vmerrs"
    20  	"github.com/ethereum/go-ethereum/common"
    21  )
    22  
    23  var (
    24  	allowedFutureBlockTime = 10 * time.Second // Max time from current time allowed for blocks, before they're considered future blocks
    25  
    26  	errInvalidBlockTime     = errors.New("timestamp less than parent's")
    27  	errUnclesUnsupported    = errors.New("uncles unsupported")
    28  	errBlockGasCostNil      = errors.New("block gas cost is nil")
    29  	errBlockGasCostTooLarge = errors.New("block gas cost is not uint64")
    30  	errBaseFeeNil           = errors.New("base fee is nil")
    31  )
    32  
    33  type Mode struct {
    34  	ModeSkipHeader   bool
    35  	ModeSkipBlockFee bool
    36  	ModeSkipCoinbase bool
    37  }
    38  
    39  type (
    40  	DummyEngine struct {
    41  		clock         *mockable.Clock
    42  		consensusMode Mode
    43  	}
    44  )
    45  
    46  func NewETHFaker() *DummyEngine {
    47  	return &DummyEngine{
    48  		clock:         &mockable.Clock{},
    49  		consensusMode: Mode{ModeSkipBlockFee: true},
    50  	}
    51  }
    52  
    53  func NewFaker() *DummyEngine {
    54  	return &DummyEngine{
    55  		clock: &mockable.Clock{},
    56  	}
    57  }
    58  
    59  func NewFakerWithClock(clock *mockable.Clock) *DummyEngine {
    60  	return &DummyEngine{
    61  		clock: clock,
    62  	}
    63  }
    64  
    65  func NewFakerWithMode(mode Mode) *DummyEngine {
    66  	return &DummyEngine{
    67  		clock:         &mockable.Clock{},
    68  		consensusMode: mode,
    69  	}
    70  }
    71  
    72  func NewCoinbaseFaker() *DummyEngine {
    73  	return &DummyEngine{
    74  		clock:         &mockable.Clock{},
    75  		consensusMode: Mode{ModeSkipCoinbase: true},
    76  	}
    77  }
    78  
    79  func NewFullFaker() *DummyEngine {
    80  	return &DummyEngine{
    81  		clock:         &mockable.Clock{},
    82  		consensusMode: Mode{ModeSkipHeader: true},
    83  	}
    84  }
    85  
    86  // verifyCoinbase checks that the coinbase is valid for the given [header] and [parent].
    87  func (self *DummyEngine) verifyCoinbase(config *params.ChainConfig, header *types.Header, parent *types.Header, chain consensus.ChainHeaderReader) error {
    88  	if self.consensusMode.ModeSkipCoinbase {
    89  		return nil
    90  	}
    91  	// get the coinbase configured at parent
    92  	configuredAddressAtParent, isAllowFeeRecipients, err := chain.GetCoinbaseAt(parent)
    93  	if err != nil {
    94  		return fmt.Errorf("failed to get coinbase at %v: %w", header.Hash(), err)
    95  	}
    96  
    97  	if isAllowFeeRecipients {
    98  		// if fee recipients are allowed we don't need to check the coinbase
    99  		return nil
   100  	}
   101  	// we fetch the configured coinbase at the parent's state
   102  	// to check against the coinbase in [header].
   103  	if configuredAddressAtParent != header.Coinbase {
   104  		return fmt.Errorf("%w: %v does not match required coinbase address %v", vmerrs.ErrInvalidCoinbase, header.Coinbase, configuredAddressAtParent)
   105  	}
   106  	return nil
   107  }
   108  
   109  func (self *DummyEngine) verifyHeaderGasFields(config *params.ChainConfig, header *types.Header, parent *types.Header, chain consensus.ChainHeaderReader) error {
   110  	timestamp := new(big.Int).SetUint64(header.Time)
   111  
   112  	// Verify that the gas limit is <= 2^63-1
   113  	if header.GasLimit > params.MaxGasLimit {
   114  		return fmt.Errorf("invalid gasLimit: have %v, max %v", header.GasLimit, params.MaxGasLimit)
   115  	}
   116  	// Verify that the gasUsed is <= gasLimit
   117  	if header.GasUsed > header.GasLimit {
   118  		return fmt.Errorf("invalid gasUsed: have %d, gasLimit %d", header.GasUsed, header.GasLimit)
   119  	}
   120  	// We verify the current block by checking the parent fee config
   121  	// this is because the current block cannot set the fee config for itself
   122  	// Fee config might depend on the state when precompile is activated
   123  	// but we don't know the final state while forming the block.
   124  	// See worker package for more details.
   125  	feeConfig, _, err := chain.GetFeeConfigAt(parent)
   126  	if err != nil {
   127  		return err
   128  	}
   129  	if config.IsSubnetEVM(timestamp) {
   130  		expectedGasLimit := feeConfig.GasLimit.Uint64()
   131  		if header.GasLimit != expectedGasLimit {
   132  			return fmt.Errorf("expected gas limit to be %d, but found %d", expectedGasLimit, header.GasLimit)
   133  		}
   134  	} else {
   135  		// Verify that the gas limit remains within allowed bounds
   136  		diff := int64(parent.GasLimit) - int64(header.GasLimit)
   137  		if diff < 0 {
   138  			diff *= -1
   139  		}
   140  		limit := parent.GasLimit / params.GasLimitBoundDivisor
   141  
   142  		if uint64(diff) >= limit || header.GasLimit < params.MinGasLimit {
   143  			return fmt.Errorf("invalid gas limit: have %d, want %d += %d", header.GasLimit, parent.GasLimit, limit)
   144  		}
   145  		// Verify BaseFee is not present before Subnet EVM
   146  		if header.BaseFee != nil {
   147  			return fmt.Errorf("invalid baseFee before fork: have %d, want <nil>", header.BaseFee)
   148  		}
   149  		if header.BlockGasCost != nil {
   150  			return fmt.Errorf("invalid blockGasCost before fork: have %d, want <nil>", header.BlockGasCost)
   151  		}
   152  		return nil
   153  	}
   154  
   155  	// Verify baseFee and rollupWindow encoding as part of header verification
   156  	// starting in Subnet EVM
   157  	expectedRollupWindowBytes, expectedBaseFee, err := CalcBaseFee(config, feeConfig, parent, header.Time)
   158  	if err != nil {
   159  		return fmt.Errorf("failed to calculate base fee: %w", err)
   160  	}
   161  	if !bytes.Equal(expectedRollupWindowBytes, header.Extra) {
   162  		return fmt.Errorf("expected rollup window bytes: %x, found %x", expectedRollupWindowBytes, header.Extra)
   163  	}
   164  	if header.BaseFee == nil {
   165  		return errors.New("expected baseFee to be non-nil")
   166  	}
   167  	if bfLen := header.BaseFee.BitLen(); bfLen > 256 {
   168  		return fmt.Errorf("too large base fee: bitlen %d", bfLen)
   169  	}
   170  	if header.BaseFee.Cmp(expectedBaseFee) != 0 {
   171  		return fmt.Errorf("expected base fee (%d), found (%d)", expectedBaseFee, header.BaseFee)
   172  	}
   173  
   174  	// Enforce BlockGasCost constraints
   175  	expectedBlockGasCost := calcBlockGasCost(
   176  		feeConfig.TargetBlockRate,
   177  		feeConfig.MinBlockGasCost,
   178  		feeConfig.MaxBlockGasCost,
   179  		feeConfig.BlockGasCostStep,
   180  		parent.BlockGasCost,
   181  		parent.Time, header.Time,
   182  	)
   183  	if header.BlockGasCost == nil {
   184  		return errBlockGasCostNil
   185  	}
   186  	if !header.BlockGasCost.IsUint64() {
   187  		return errBlockGasCostTooLarge
   188  	}
   189  	if header.BlockGasCost.Cmp(expectedBlockGasCost) != 0 {
   190  		return fmt.Errorf("invalid block gas cost: have %d, want %d", header.BlockGasCost, expectedBlockGasCost)
   191  	}
   192  	return nil
   193  }
   194  
   195  // modified from consensus.go
   196  func (self *DummyEngine) verifyHeader(chain consensus.ChainHeaderReader, header *types.Header, parent *types.Header, uncle bool) error {
   197  	var (
   198  		config    = chain.Config()
   199  		timestamp = new(big.Int).SetUint64(header.Time)
   200  	)
   201  	// Ensure that we do not verify an uncle
   202  	if uncle {
   203  		return errUnclesUnsupported
   204  	}
   205  	// Ensure that the header's extra-data section is of a reasonable size
   206  	if !config.IsSubnetEVM(timestamp) {
   207  		if uint64(len(header.Extra)) > params.MaximumExtraDataSize {
   208  			return fmt.Errorf("extra-data too long: %d > %d", len(header.Extra), params.MaximumExtraDataSize)
   209  		}
   210  	} else {
   211  		expectedExtraDataSize := params.ExtraDataSize
   212  		if len(header.Extra) != expectedExtraDataSize {
   213  			return fmt.Errorf("expected extra-data field to be: %d, but found %d", expectedExtraDataSize, len(header.Extra))
   214  		}
   215  	}
   216  	// Ensure gas-related header fields are correct
   217  	if err := self.verifyHeaderGasFields(config, header, parent, chain); err != nil {
   218  		return err
   219  	}
   220  	// Ensure that coinbase is valid
   221  	if err := self.verifyCoinbase(config, header, parent, chain); err != nil {
   222  		return err
   223  	}
   224  
   225  	// Verify the header's timestamp
   226  	if header.Time > uint64(self.clock.Time().Add(allowedFutureBlockTime).Unix()) {
   227  		return consensus.ErrFutureBlock
   228  	}
   229  	// Verify the header's timestamp is not earlier than parent's
   230  	// it does include equality(==), so multiple blocks per second is ok
   231  	if header.Time < parent.Time {
   232  		return errInvalidBlockTime
   233  	}
   234  	// Verify that the block number is parent's +1
   235  	if diff := new(big.Int).Sub(header.Number, parent.Number); diff.Cmp(big.NewInt(1)) != 0 {
   236  		return consensus.ErrInvalidNumber
   237  	}
   238  	return nil
   239  }
   240  
   241  func (self *DummyEngine) Author(header *types.Header) (common.Address, error) {
   242  	return header.Coinbase, nil
   243  }
   244  
   245  func (self *DummyEngine) VerifyHeader(chain consensus.ChainHeaderReader, header *types.Header) error {
   246  	// If we're running a full engine faking, accept any input as valid
   247  	if self.consensusMode.ModeSkipHeader {
   248  		return nil
   249  	}
   250  	// Short circuit if the header is known, or it's parent not
   251  	number := header.Number.Uint64()
   252  	if chain.GetHeader(header.Hash(), number) != nil {
   253  		return nil
   254  	}
   255  	parent := chain.GetHeader(header.ParentHash, number-1)
   256  	if parent == nil {
   257  		return consensus.ErrUnknownAncestor
   258  	}
   259  	// Sanity checks passed, do a proper verification
   260  	return self.verifyHeader(chain, header, parent, false)
   261  }
   262  
   263  func (self *DummyEngine) VerifyUncles(chain consensus.ChainReader, block *types.Block) error {
   264  	if len(block.Uncles()) > 0 {
   265  		return errUnclesUnsupported
   266  	}
   267  	return nil
   268  }
   269  
   270  func (self *DummyEngine) Prepare(chain consensus.ChainHeaderReader, header *types.Header) error {
   271  	header.Difficulty = big.NewInt(1)
   272  	return nil
   273  }
   274  
   275  func (self *DummyEngine) verifyBlockFee(
   276  	baseFee *big.Int,
   277  	requiredBlockGasCost *big.Int,
   278  	txs []*types.Transaction,
   279  	receipts []*types.Receipt,
   280  ) error {
   281  	if self.consensusMode.ModeSkipBlockFee {
   282  		return nil
   283  	}
   284  	if baseFee == nil || baseFee.Sign() <= 0 {
   285  		return fmt.Errorf("invalid base fee (%d) in SubnetEVM", baseFee)
   286  	}
   287  	if requiredBlockGasCost == nil || !requiredBlockGasCost.IsUint64() {
   288  		return fmt.Errorf("invalid block gas cost (%d) in SubnetEVM", requiredBlockGasCost)
   289  	}
   290  
   291  	var (
   292  		gasUsed              = new(big.Int)
   293  		blockFeeContribution = new(big.Int)
   294  		totalBlockFee        = new(big.Int)
   295  	)
   296  	// Calculate the total excess over the base fee that was paid towards the block fee
   297  	for i, receipt := range receipts {
   298  		// Each transaction contributes the excess over the baseFee towards the totalBlockFee
   299  		// This should be equivalent to the sum of the "priority fees" within EIP-1559.
   300  		txFeePremium, err := txs[i].EffectiveGasTip(baseFee)
   301  		if err != nil {
   302  			return err
   303  		}
   304  		// Multiply the [txFeePremium] by the gasUsed in the transaction since this gives the total coin that was paid
   305  		// above the amount required if the transaction had simply paid the minimum base fee for the block.
   306  		//
   307  		// Ex. LegacyTx paying a gas price of 100 gwei for 1M gas in a block with a base fee of 10 gwei.
   308  		// Total Fee = 100 gwei * 1M gas
   309  		// Minimum Fee = 10 gwei * 1M gas (minimum fee that would have been accepted for this transaction)
   310  		// Fee Premium = 90 gwei
   311  		// Total Overpaid = 90 gwei * 1M gas
   312  
   313  		blockFeeContribution.Mul(txFeePremium, gasUsed.SetUint64(receipt.GasUsed))
   314  		totalBlockFee.Add(totalBlockFee, blockFeeContribution)
   315  	}
   316  	// Calculate how much gas the [totalBlockFee] would purchase at the price level
   317  	// set by the base fee of this block.
   318  	blockGas := new(big.Int).Div(totalBlockFee, baseFee)
   319  
   320  	// Require that the amount of gas purchased by the effective tips within the block, [blockGas],
   321  	// covers at least [requiredBlockGasCost].
   322  	//
   323  	// NOTE: To determine the [requiredBlockFee], multiply [requiredBlockGasCost]
   324  	// by [baseFee].
   325  	if blockGas.Cmp(requiredBlockGasCost) < 0 {
   326  		return fmt.Errorf(
   327  			"insufficient gas (%d) to cover the block cost (%d) at base fee (%d) (total block fee: %d)",
   328  			blockGas, requiredBlockGasCost, baseFee, totalBlockFee,
   329  		)
   330  	}
   331  	return nil
   332  }
   333  
   334  func (self *DummyEngine) Finalize(chain consensus.ChainHeaderReader, block *types.Block, parent *types.Header, state *state.StateDB, receipts []*types.Receipt) error {
   335  	if chain.Config().IsSubnetEVM(new(big.Int).SetUint64(block.Time())) {
   336  		// we use the parent to determine the fee config
   337  		// since the current block has not been finalized yet.
   338  		feeConfig, _, err := chain.GetFeeConfigAt(parent)
   339  		if err != nil {
   340  			return err
   341  		}
   342  
   343  		// Calculate the expected blockGasCost for this block.
   344  		// Note: this is a deterministic transtion that defines an exact block fee for this block.
   345  		blockGasCost := calcBlockGasCost(
   346  			feeConfig.TargetBlockRate,
   347  			feeConfig.MinBlockGasCost,
   348  			feeConfig.MaxBlockGasCost,
   349  			feeConfig.BlockGasCostStep,
   350  			parent.BlockGasCost,
   351  			parent.Time, block.Time(),
   352  		)
   353  		// Verify the BlockGasCost set in the header matches the calculated value.
   354  		if blockBlockGasCost := block.BlockGasCost(); blockBlockGasCost == nil || !blockBlockGasCost.IsUint64() || blockBlockGasCost.Cmp(blockGasCost) != 0 {
   355  			return fmt.Errorf("invalid blockGasCost: have %d, want %d", blockBlockGasCost, blockGasCost)
   356  		}
   357  		// Verify the block fee was paid.
   358  		if err := self.verifyBlockFee(
   359  			block.BaseFee(),
   360  			block.BlockGasCost(),
   361  			block.Transactions(),
   362  			receipts,
   363  		); err != nil {
   364  			return err
   365  		}
   366  	}
   367  
   368  	return nil
   369  }
   370  
   371  func (self *DummyEngine) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, parent *types.Header, state *state.StateDB, txs []*types.Transaction,
   372  	uncles []*types.Header, receipts []*types.Receipt,
   373  ) (*types.Block, error) {
   374  	if chain.Config().IsSubnetEVM(new(big.Int).SetUint64(header.Time)) {
   375  		// we use the parent to determine the fee config
   376  		// since the current block has not been finalized yet.
   377  		feeConfig, _, err := chain.GetFeeConfigAt(parent)
   378  		if err != nil {
   379  			return nil, err
   380  		}
   381  		// Calculate the required block gas cost for this block.
   382  		header.BlockGasCost = calcBlockGasCost(
   383  			feeConfig.TargetBlockRate,
   384  			feeConfig.MinBlockGasCost,
   385  			feeConfig.MaxBlockGasCost,
   386  			feeConfig.BlockGasCostStep,
   387  			parent.BlockGasCost,
   388  			parent.Time, header.Time,
   389  		)
   390  		// Verify that this block covers the block fee.
   391  		if err := self.verifyBlockFee(
   392  			header.BaseFee,
   393  			header.BlockGasCost,
   394  			txs,
   395  			receipts,
   396  		); err != nil {
   397  			return nil, err
   398  		}
   399  	}
   400  	// commit the final state root
   401  	header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number))
   402  
   403  	// Header seems complete, assemble into a block and return
   404  	return types.NewBlock(
   405  		header, txs, uncles, receipts, new(trie.Trie),
   406  	), nil
   407  }
   408  
   409  func (self *DummyEngine) CalcDifficulty(chain consensus.ChainHeaderReader, time uint64, parent *types.Header) *big.Int {
   410  	return big.NewInt(1)
   411  }
   412  
   413  func (self *DummyEngine) Close() error {
   414  	return nil
   415  }