gitlab.com/flarenetwork/coreth@v0.1.1/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/ava-labs/avalanchego/utils/wrappers"
    12  	"github.com/ethereum/go-ethereum/common"
    13  	"github.com/ethereum/go-ethereum/common/math"
    14  	"gitlab.com/flarenetwork/coreth/core/types"
    15  	"gitlab.com/flarenetwork/coreth/params"
    16  )
    17  
    18  var (
    19  	MaxBaseFee          = big.NewInt(params.ApricotPhase3MaxBaseFee)
    20  	MinBaseFee          = big.NewInt(params.ApricotPhase3MinBaseFee)
    21  	TargetGas    uint64 = 10_000_000
    22  	BlockGasFee  uint64 = 1_000_000
    23  	rollupWindow uint64 = 10
    24  )
    25  
    26  // CalcBaseFee takes the previous header and the timestamp of its child block
    27  // and calculates the expected base fee as well as the encoding of the past
    28  // pricing information for the child block.
    29  // CalcBaseFee should only be called if [timestamp] >= [config.ApricotPhase3Timestamp]
    30  func CalcBaseFee(config *params.ChainConfig, parent *types.Header, timestamp uint64) ([]byte, *big.Int, error) {
    31  	// If the current block is the first EIP-1559 block, or it is the genesis block
    32  	// return the initial slice and initial base fee.
    33  	if !config.IsApricotPhase3(new(big.Int).SetUint64(parent.Time)) || parent.Number.Cmp(common.Big0) == 0 {
    34  		initialSlice := make([]byte, params.ApricotPhase3ExtraDataSize)
    35  		initialBaseFee := big.NewInt(params.ApricotPhase3InitialBaseFee)
    36  		return initialSlice, initialBaseFee, nil
    37  	}
    38  	if len(parent.Extra) != params.ApricotPhase3ExtraDataSize {
    39  		return nil, nil, fmt.Errorf("expected length of parent extra data to be %d, but found %d", params.ApricotPhase3ExtraDataSize, len(parent.Extra))
    40  	}
    41  
    42  	if timestamp < parent.Time {
    43  		return nil, nil, fmt.Errorf("cannot calculate base fee for timestamp (%d) prior to parent timestamp (%d)", timestamp, parent.Time)
    44  	}
    45  	roll := timestamp - parent.Time
    46  
    47  	// roll the window over by the difference between the timestamps to generate
    48  	// the new rollup window.
    49  	newRollupWindow, err := rollLongWindow(parent.Extra, int(roll))
    50  	if err != nil {
    51  		return nil, nil, err
    52  	}
    53  
    54  	var (
    55  		parentGasTarget          = TargetGas
    56  		parentGasTargetBig       = new(big.Int).SetUint64(parentGasTarget)
    57  		baseFeeChangeDenominator = new(big.Int).SetUint64(params.BaseFeeChangeDenominator)
    58  		baseFee                  = new(big.Int).Set(parent.BaseFee)
    59  	)
    60  
    61  	// Add in the gas used by the parent block in the correct place
    62  	// If the parent consumed gas within the rollup window, add the consumed
    63  	// gas in.
    64  	if roll < rollupWindow {
    65  		addedGas, overflow := math.SafeAdd(parent.GasUsed, BlockGasFee)
    66  		if overflow {
    67  			addedGas = math.MaxUint64
    68  		}
    69  		slot := rollupWindow - 1 - roll
    70  		start := slot * wrappers.LongLen
    71  		updateLongWindow(newRollupWindow, start, addedGas)
    72  	}
    73  
    74  	// Calculate the amount of gas consumed within the rollup window.
    75  	totalGas := sumLongWindow(newRollupWindow, int(rollupWindow))
    76  
    77  	if totalGas == parentGasTarget {
    78  		return newRollupWindow, baseFee, nil
    79  	}
    80  
    81  	if totalGas > parentGasTarget {
    82  		// If the parent block used more gas than its target, the baseFee should increase.
    83  		gasUsedDelta := new(big.Int).SetUint64(totalGas - parentGasTarget)
    84  		x := new(big.Int).Mul(parent.BaseFee, gasUsedDelta)
    85  		y := x.Div(x, parentGasTargetBig)
    86  		baseFeeDelta := math.BigMax(
    87  			x.Div(y, baseFeeChangeDenominator),
    88  			common.Big1,
    89  		)
    90  
    91  		// Gas price is increasing, so ensure it does not increase past the maximum
    92  		baseFee.Add(baseFee, baseFeeDelta)
    93  		baseFee = selectBigWithinBounds(MinBaseFee, baseFee, MaxBaseFee)
    94  	} else {
    95  		// Otherwise if the parent block used less gas than its target, the baseFee should decrease.
    96  		gasUsedDelta := new(big.Int).SetUint64(parentGasTarget - totalGas)
    97  		x := new(big.Int).Mul(parent.BaseFee, gasUsedDelta)
    98  		y := x.Div(x, parentGasTargetBig)
    99  		baseFeeDelta := math.BigMax(
   100  			x.Div(y, baseFeeChangeDenominator),
   101  			common.Big1,
   102  		)
   103  
   104  		// If [roll] is greater than [rollupWindow], apply the state transition to the base fee to account
   105  		// for the interval during which no blocks were produced.
   106  		// We use roll/rollupWindow, so that the transition is applied for every [rollupWindow] seconds
   107  		// that has elapsed between the parent and this block.
   108  		if roll > rollupWindow {
   109  			// Note: roll/rollupWindow must be greater than 1 since we've checked that roll > rollupWindow
   110  			baseFeeDelta = baseFeeDelta.Mul(baseFeeDelta, new(big.Int).SetUint64(roll/rollupWindow))
   111  		}
   112  		baseFee = selectBigWithinBounds(MinBaseFee, baseFee.Sub(baseFee, baseFeeDelta), MaxBaseFee)
   113  	}
   114  
   115  	return newRollupWindow, baseFee, nil
   116  }
   117  
   118  // selectBigWithinBounds returns [value] if it is within the bounds:
   119  // lowerBound <= value <= upperBound or the bound at either end if [value]
   120  // is outside of the defined boundaries.
   121  func selectBigWithinBounds(lowerBound, value, upperBound *big.Int) *big.Int {
   122  	switch {
   123  	case value.Cmp(lowerBound) < 0:
   124  		return new(big.Int).Set(lowerBound)
   125  	case value.Cmp(upperBound) > 0:
   126  		return new(big.Int).Set(upperBound)
   127  	default:
   128  		return value
   129  	}
   130  }
   131  
   132  // rollWindow rolls the longs within [consumptionWindow] over by [roll] places.
   133  // For example, if there are 4 longs encoded in a 32 byte slice, rollWindow would
   134  // have the following effect:
   135  // Original:
   136  // [1, 2, 3, 4]
   137  // Roll = 0
   138  // [1, 2, 3, 4]
   139  // Roll = 1
   140  // [2, 3, 4, 0]
   141  // Roll = 2
   142  // [3, 4, 0, 0]
   143  // Roll = 3
   144  // [4, 0, 0, 0]
   145  // Roll >= 4
   146  // [0, 0, 0, 0]
   147  // Assumes that [roll] is greater than or equal to 0
   148  func rollWindow(consumptionWindow []byte, size, roll int) ([]byte, error) {
   149  	if len(consumptionWindow)%size != 0 {
   150  		return nil, fmt.Errorf("expected consumption window length (%d) to be a multiple of size (%d)", len(consumptionWindow), size)
   151  	}
   152  
   153  	// Note: make allocates a zeroed array, so we are guaranteed
   154  	// that what we do not copy into, will be set to 0
   155  	res := make([]byte, len(consumptionWindow))
   156  	bound := roll * size
   157  	if bound > len(consumptionWindow) {
   158  		return res, nil
   159  	}
   160  	copy(res[:], consumptionWindow[roll*size:])
   161  	return res, nil
   162  }
   163  
   164  func rollLongWindow(consumptionWindow []byte, roll int) ([]byte, error) {
   165  	// Passes in [wrappers.LongLen] as the size of the individual value to be rolled over
   166  	// so that it can be used to roll an array of long values.
   167  	return rollWindow(consumptionWindow, wrappers.LongLen, roll)
   168  }
   169  
   170  // sumLongWindow sums [numLongs] encoded in [window]. Assumes that the length of [window]
   171  // is sufficient to contain [numLongs] or else this function panics.
   172  // If an overflow occurs, while summing the contents, the maximum uint64 value is returned.
   173  func sumLongWindow(window []byte, numLongs int) uint64 {
   174  	var (
   175  		sum      uint64 = 0
   176  		overflow bool
   177  	)
   178  	for i := 0; i < numLongs; i++ {
   179  		// If an overflow occurs while summing the elements of the window, return the maximum
   180  		// uint64 value immediately.
   181  		sum, overflow = math.SafeAdd(sum, binary.BigEndian.Uint64(window[wrappers.LongLen*i:]))
   182  		if overflow {
   183  			return math.MaxUint64
   184  		}
   185  	}
   186  	return sum
   187  }
   188  
   189  // updateLongWindow adds [gasConsumed] in at index within [window].
   190  // Assumes that [index] has already been validated.
   191  // If an overflow occurs, the maximum uint64 value is used.
   192  func updateLongWindow(window []byte, start uint64, gasConsumed uint64) {
   193  	prevGasConsumed := binary.BigEndian.Uint64(window[start:])
   194  
   195  	totalGasConsumed, overflow := math.SafeAdd(prevGasConsumed, gasConsumed)
   196  	if overflow {
   197  		totalGasConsumed = math.MaxUint64
   198  	}
   199  	binary.BigEndian.PutUint64(window[start:], totalGasConsumed)
   200  }