github.com/dim4egster/coreth@v0.10.2/consensus/dummy/dynamic_fees.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  	"encoding/binary"
     8  	"fmt"
     9  	"math/big"
    10  
    11  	"github.com/dim4egster/qmallgo/utils/wrappers"
    12  	"github.com/dim4egster/coreth/core/types"
    13  	"github.com/dim4egster/coreth/params"
    14  	"github.com/ethereum/go-ethereum/common"
    15  	"github.com/ethereum/go-ethereum/common/math"
    16  )
    17  
    18  var (
    19  	ApricotPhase3MinBaseFee = big.NewInt(params.ApricotPhase3MinBaseFee)
    20  	ApricotPhase3MaxBaseFee = big.NewInt(params.ApricotPhase3MaxBaseFee)
    21  	ApricotPhase4MinBaseFee = big.NewInt(params.ApricotPhase4MinBaseFee)
    22  	ApricotPhase4MaxBaseFee = big.NewInt(params.ApricotPhase4MaxBaseFee)
    23  
    24  	ApricotPhase4BaseFeeChangeDenominator = new(big.Int).SetUint64(params.ApricotPhase4BaseFeeChangeDenominator)
    25  	ApricotPhase5BaseFeeChangeDenominator = new(big.Int).SetUint64(params.ApricotPhase5BaseFeeChangeDenominator)
    26  
    27  	ApricotPhase3BlockGasFee      uint64 = 1_000_000
    28  	ApricotPhase4MinBlockGasCost         = new(big.Int).Set(common.Big0)
    29  	ApricotPhase4MaxBlockGasCost         = big.NewInt(1_000_000)
    30  	ApricotPhase4BlockGasCostStep        = big.NewInt(50_000)
    31  	ApricotPhase4TargetBlockRate  uint64 = 2 // in seconds
    32  	ApricotPhase5BlockGasCostStep        = big.NewInt(200_000)
    33  	rollupWindow                  uint64 = 10
    34  )
    35  
    36  // CalcBaseFee takes the previous header and the timestamp of its child block
    37  // and calculates the expected base fee as well as the encoding of the past
    38  // pricing information for the child block.
    39  // CalcBaseFee should only be called if [timestamp] >= [config.ApricotPhase3Timestamp]
    40  func CalcBaseFee(config *params.ChainConfig, parent *types.Header, timestamp uint64) ([]byte, *big.Int, error) {
    41  	// If the current block is the first EIP-1559 block, or it is the genesis block
    42  	// return the initial slice and initial base fee.
    43  	bigTimestamp := new(big.Int).SetUint64(parent.Time)
    44  	var (
    45  		isApricotPhase3 = config.IsApricotPhase3(bigTimestamp)
    46  		isApricotPhase4 = config.IsApricotPhase4(bigTimestamp)
    47  		isApricotPhase5 = config.IsApricotPhase5(bigTimestamp)
    48  	)
    49  	if !isApricotPhase3 || parent.Number.Cmp(common.Big0) == 0 {
    50  		initialSlice := make([]byte, params.ApricotPhase3ExtraDataSize)
    51  		initialBaseFee := big.NewInt(params.ApricotPhase3InitialBaseFee)
    52  		return initialSlice, initialBaseFee, nil
    53  	}
    54  	if uint64(len(parent.Extra)) != params.ApricotPhase3ExtraDataSize {
    55  		return nil, nil, fmt.Errorf("expected length of parent extra data to be %d, but found %d", params.ApricotPhase3ExtraDataSize, len(parent.Extra))
    56  	}
    57  
    58  	if timestamp < parent.Time {
    59  		return nil, nil, fmt.Errorf("cannot calculate base fee for timestamp (%d) prior to parent timestamp (%d)", timestamp, parent.Time)
    60  	}
    61  	roll := timestamp - parent.Time
    62  
    63  	// roll the window over by the difference between the timestamps to generate
    64  	// the new rollup window.
    65  	newRollupWindow, err := rollLongWindow(parent.Extra, int(roll))
    66  	if err != nil {
    67  		return nil, nil, err
    68  	}
    69  
    70  	// If AP5, use a less responsive [BaseFeeChangeDenominator] and a higher gas
    71  	// block limit
    72  	var (
    73  		baseFee                  = new(big.Int).Set(parent.BaseFee)
    74  		baseFeeChangeDenominator = ApricotPhase4BaseFeeChangeDenominator
    75  		parentGasTarget          = params.ApricotPhase3TargetGas
    76  	)
    77  	if isApricotPhase5 {
    78  		baseFeeChangeDenominator = ApricotPhase5BaseFeeChangeDenominator
    79  		parentGasTarget = params.ApricotPhase5TargetGas
    80  	}
    81  	parentGasTargetBig := new(big.Int).SetUint64(parentGasTarget)
    82  
    83  	// Add in the gas used by the parent block in the correct place
    84  	// If the parent consumed gas within the rollup window, add the consumed
    85  	// gas in.
    86  	if roll < rollupWindow {
    87  		var blockGasCost, parentExtraStateGasUsed uint64
    88  		switch {
    89  		case isApricotPhase5:
    90  			// [blockGasCost] has been removed in AP5, so it is left as 0.
    91  
    92  			// At the start of a new network, the parent
    93  			// may not have a populated [ExtDataGasUsed].
    94  			if parent.ExtDataGasUsed != nil {
    95  				parentExtraStateGasUsed = parent.ExtDataGasUsed.Uint64()
    96  			}
    97  		case isApricotPhase4:
    98  			// The [blockGasCost] is paid by the effective tips in the block using
    99  			// the block's value of [baseFee].
   100  			blockGasCost = calcBlockGasCost(
   101  				ApricotPhase4TargetBlockRate,
   102  				ApricotPhase4MinBlockGasCost,
   103  				ApricotPhase4MaxBlockGasCost,
   104  				ApricotPhase4BlockGasCostStep,
   105  				parent.BlockGasCost,
   106  				parent.Time, timestamp,
   107  			).Uint64()
   108  
   109  			// On the boundary of AP3 and AP4 or at the start of a new network, the parent
   110  			// may not have a populated [ExtDataGasUsed].
   111  			if parent.ExtDataGasUsed != nil {
   112  				parentExtraStateGasUsed = parent.ExtDataGasUsed.Uint64()
   113  			}
   114  		default:
   115  			blockGasCost = ApricotPhase3BlockGasFee
   116  		}
   117  
   118  		// Compute the new state of the gas rolling window.
   119  		addedGas, overflow := math.SafeAdd(parent.GasUsed, parentExtraStateGasUsed)
   120  		if overflow {
   121  			addedGas = math.MaxUint64
   122  		}
   123  
   124  		// Only add the [blockGasCost] to the gas used if it isn't AP5
   125  		if !isApricotPhase5 {
   126  			addedGas, overflow = math.SafeAdd(addedGas, blockGasCost)
   127  			if overflow {
   128  				addedGas = math.MaxUint64
   129  			}
   130  		}
   131  
   132  		slot := rollupWindow - 1 - roll
   133  		start := slot * wrappers.LongLen
   134  		updateLongWindow(newRollupWindow, start, addedGas)
   135  	}
   136  
   137  	// Calculate the amount of gas consumed within the rollup window.
   138  	totalGas := sumLongWindow(newRollupWindow, int(rollupWindow))
   139  
   140  	if totalGas == parentGasTarget {
   141  		return newRollupWindow, baseFee, nil
   142  	}
   143  
   144  	if totalGas > parentGasTarget {
   145  		// If the parent block used more gas than its target, the baseFee should increase.
   146  		gasUsedDelta := new(big.Int).SetUint64(totalGas - parentGasTarget)
   147  		x := new(big.Int).Mul(parent.BaseFee, gasUsedDelta)
   148  		y := x.Div(x, parentGasTargetBig)
   149  		baseFeeDelta := math.BigMax(
   150  			x.Div(y, baseFeeChangeDenominator),
   151  			common.Big1,
   152  		)
   153  
   154  		baseFee.Add(baseFee, baseFeeDelta)
   155  	} else {
   156  		// Otherwise if the parent block used less gas than its target, the baseFee should decrease.
   157  		gasUsedDelta := new(big.Int).SetUint64(parentGasTarget - totalGas)
   158  		x := new(big.Int).Mul(parent.BaseFee, gasUsedDelta)
   159  		y := x.Div(x, parentGasTargetBig)
   160  		baseFeeDelta := math.BigMax(
   161  			x.Div(y, baseFeeChangeDenominator),
   162  			common.Big1,
   163  		)
   164  
   165  		// If [roll] is greater than [rollupWindow], apply the state transition to the base fee to account
   166  		// for the interval during which no blocks were produced.
   167  		// We use roll/rollupWindow, so that the transition is applied for every [rollupWindow] seconds
   168  		// that has elapsed between the parent and this block.
   169  		if roll > rollupWindow {
   170  			// Note: roll/rollupWindow must be greater than 1 since we've checked that roll > rollupWindow
   171  			baseFeeDelta = baseFeeDelta.Mul(baseFeeDelta, new(big.Int).SetUint64(roll/rollupWindow))
   172  		}
   173  		baseFee.Sub(baseFee, baseFeeDelta)
   174  	}
   175  
   176  	// Ensure that the base fee does not increase/decrease outside of the bounds
   177  	switch {
   178  	case isApricotPhase5:
   179  		baseFee = selectBigWithinBounds(ApricotPhase4MinBaseFee, baseFee, nil)
   180  	case isApricotPhase4:
   181  		baseFee = selectBigWithinBounds(ApricotPhase4MinBaseFee, baseFee, ApricotPhase4MaxBaseFee)
   182  	default:
   183  		baseFee = selectBigWithinBounds(ApricotPhase3MinBaseFee, baseFee, ApricotPhase3MaxBaseFee)
   184  	}
   185  
   186  	return newRollupWindow, baseFee, nil
   187  }
   188  
   189  // EstiamteNextBaseFee attempts to estimate the next base fee based on a block with [parent] being built at
   190  // [timestamp].
   191  // If [timestamp] is less than the timestamp of [parent], then it uses the same timestamp as parent.
   192  // Warning: This function should only be used in estimation and should not be used when calculating the canonical
   193  // base fee for a subsequent block.
   194  func EstimateNextBaseFee(config *params.ChainConfig, parent *types.Header, timestamp uint64) ([]byte, *big.Int, error) {
   195  	if timestamp < parent.Time {
   196  		timestamp = parent.Time
   197  	}
   198  	return CalcBaseFee(config, parent, timestamp)
   199  }
   200  
   201  // selectBigWithinBounds returns [value] if it is within the bounds:
   202  // lowerBound <= value <= upperBound or the bound at either end if [value]
   203  // is outside of the defined boundaries.
   204  func selectBigWithinBounds(lowerBound, value, upperBound *big.Int) *big.Int {
   205  	switch {
   206  	case lowerBound != nil && value.Cmp(lowerBound) < 0:
   207  		return new(big.Int).Set(lowerBound)
   208  	case upperBound != nil && value.Cmp(upperBound) > 0:
   209  		return new(big.Int).Set(upperBound)
   210  	default:
   211  		return value
   212  	}
   213  }
   214  
   215  // rollWindow rolls the longs within [consumptionWindow] over by [roll] places.
   216  // For example, if there are 4 longs encoded in a 32 byte slice, rollWindow would
   217  // have the following effect:
   218  // Original:
   219  // [1, 2, 3, 4]
   220  // Roll = 0
   221  // [1, 2, 3, 4]
   222  // Roll = 1
   223  // [2, 3, 4, 0]
   224  // Roll = 2
   225  // [3, 4, 0, 0]
   226  // Roll = 3
   227  // [4, 0, 0, 0]
   228  // Roll >= 4
   229  // [0, 0, 0, 0]
   230  // Assumes that [roll] is greater than or equal to 0
   231  func rollWindow(consumptionWindow []byte, size, roll int) ([]byte, error) {
   232  	if len(consumptionWindow)%size != 0 {
   233  		return nil, fmt.Errorf("expected consumption window length (%d) to be a multiple of size (%d)", len(consumptionWindow), size)
   234  	}
   235  
   236  	// Note: make allocates a zeroed array, so we are guaranteed
   237  	// that what we do not copy into, will be set to 0
   238  	res := make([]byte, len(consumptionWindow))
   239  	bound := roll * size
   240  	if bound > len(consumptionWindow) {
   241  		return res, nil
   242  	}
   243  	copy(res[:], consumptionWindow[roll*size:])
   244  	return res, nil
   245  }
   246  
   247  func rollLongWindow(consumptionWindow []byte, roll int) ([]byte, error) {
   248  	// Passes in [wrappers.LongLen] as the size of the individual value to be rolled over
   249  	// so that it can be used to roll an array of long values.
   250  	return rollWindow(consumptionWindow, wrappers.LongLen, roll)
   251  }
   252  
   253  // sumLongWindow sums [numLongs] encoded in [window]. Assumes that the length of [window]
   254  // is sufficient to contain [numLongs] or else this function panics.
   255  // If an overflow occurs, while summing the contents, the maximum uint64 value is returned.
   256  func sumLongWindow(window []byte, numLongs int) uint64 {
   257  	var (
   258  		sum      uint64 = 0
   259  		overflow bool
   260  	)
   261  	for i := 0; i < numLongs; i++ {
   262  		// If an overflow occurs while summing the elements of the window, return the maximum
   263  		// uint64 value immediately.
   264  		sum, overflow = math.SafeAdd(sum, binary.BigEndian.Uint64(window[wrappers.LongLen*i:]))
   265  		if overflow {
   266  			return math.MaxUint64
   267  		}
   268  	}
   269  	return sum
   270  }
   271  
   272  // updateLongWindow adds [gasConsumed] in at index within [window].
   273  // Assumes that [index] has already been validated.
   274  // If an overflow occurs, the maximum uint64 value is used.
   275  func updateLongWindow(window []byte, start uint64, gasConsumed uint64) {
   276  	prevGasConsumed := binary.BigEndian.Uint64(window[start:])
   277  
   278  	totalGasConsumed, overflow := math.SafeAdd(prevGasConsumed, gasConsumed)
   279  	if overflow {
   280  		totalGasConsumed = math.MaxUint64
   281  	}
   282  	binary.BigEndian.PutUint64(window[start:], totalGasConsumed)
   283  }
   284  
   285  // calcBlockGasCost calculates the required block gas cost. If [parentTime]
   286  // > [currentTime], the timeElapsed will be treated as 0.
   287  func calcBlockGasCost(
   288  	targetBlockRate uint64,
   289  	minBlockGasCost *big.Int,
   290  	maxBlockGasCost *big.Int,
   291  	blockGasCostStep *big.Int,
   292  	parentBlockGasCost *big.Int,
   293  	parentTime, currentTime uint64,
   294  ) *big.Int {
   295  	// Handle AP3/AP4 boundary by returning the minimum value as the boundary.
   296  	if parentBlockGasCost == nil {
   297  		return new(big.Int).Set(minBlockGasCost)
   298  	}
   299  
   300  	// Treat an invalid parent/current time combination as 0 elapsed time.
   301  	var timeElapsed uint64
   302  	if parentTime <= currentTime {
   303  		timeElapsed = currentTime - parentTime
   304  	}
   305  
   306  	var blockGasCost *big.Int
   307  	if timeElapsed < targetBlockRate {
   308  		blockGasCostDelta := new(big.Int).Mul(blockGasCostStep, new(big.Int).SetUint64(targetBlockRate-timeElapsed))
   309  		blockGasCost = new(big.Int).Add(parentBlockGasCost, blockGasCostDelta)
   310  	} else {
   311  		blockGasCostDelta := new(big.Int).Mul(blockGasCostStep, new(big.Int).SetUint64(timeElapsed-targetBlockRate))
   312  		blockGasCost = new(big.Int).Sub(parentBlockGasCost, blockGasCostDelta)
   313  	}
   314  
   315  	blockGasCost = selectBigWithinBounds(minBlockGasCost, blockGasCost, maxBlockGasCost)
   316  	if !blockGasCost.IsUint64() {
   317  		blockGasCost = new(big.Int).SetUint64(math.MaxUint64)
   318  	}
   319  	return blockGasCost
   320  }
   321  
   322  // MinRequiredTip is the estimated minimum tip a transaction would have
   323  // needed to pay to be included in a given block (assuming it paid a tip
   324  // proportional to its gas usage). In reality, there is no minimum tip that
   325  // is enforced by the consensus engine and high tip paying transactions can
   326  // subsidize the inclusion of low tip paying transactions. The only
   327  // correctness check performed is that the sum of all tips is >= the
   328  // required block fee.
   329  //
   330  // This function will return nil for all return values prior to Apricot Phase 4.
   331  func MinRequiredTip(config *params.ChainConfig, header *types.Header) (*big.Int, error) {
   332  	if !config.IsApricotPhase4(new(big.Int).SetUint64(header.Time)) {
   333  		return nil, nil
   334  	}
   335  	if header.BaseFee == nil {
   336  		return nil, errBaseFeeNil
   337  	}
   338  	if header.BlockGasCost == nil {
   339  		return nil, errBlockGasCostNil
   340  	}
   341  	if header.ExtDataGasUsed == nil {
   342  		return nil, errExtDataGasUsedNil
   343  	}
   344  
   345  	// minTip = requiredBlockFee/blockGasUsage
   346  	requiredBlockFee := new(big.Int).Mul(
   347  		header.BlockGasCost,
   348  		header.BaseFee,
   349  	)
   350  	blockGasUsage := new(big.Int).Add(
   351  		new(big.Int).SetUint64(header.GasUsed),
   352  		header.ExtDataGasUsed,
   353  	)
   354  	return new(big.Int).Div(requiredBlockFee, blockGasUsage), nil
   355  }