mirror of
https://github.com/luxfi/evmgpu.git
synced 2026-10-30 16:57:11 +00:00
171 lines
5.4 KiB
Go
171 lines
5.4 KiB
Go
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
|
|
package header
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"math/big"
|
|
|
|
"github.com/luxfi/evmgpu/commontype"
|
|
"github.com/luxfi/evmgpu/params/extras"
|
|
"github.com/luxfi/evmgpu/plugin/evm/upgrade/feewindow"
|
|
"github.com/luxfi/geth/common"
|
|
"github.com/luxfi/geth/core/types"
|
|
)
|
|
|
|
var (
|
|
maxUint256Plus1 = new(big.Int).Lsh(common.Big1, 256)
|
|
maxUint256 = new(big.Int).Sub(maxUint256Plus1, common.Big1)
|
|
|
|
errInvalidTimestamp = errors.New("invalid timestamp")
|
|
)
|
|
|
|
// bigMax returns the larger of x or y.
|
|
func bigMax(x, y *big.Int) *big.Int {
|
|
if x.Cmp(y) > 0 {
|
|
return x
|
|
}
|
|
return y
|
|
}
|
|
|
|
// baseFeeFromWindow should only be called if `timestamp` >= `config.EVMTimestamp`
|
|
func baseFeeFromWindow(config *extras.ChainConfig, feeConfig commontype.FeeConfig, parent *types.Header, timestamp uint64) (*big.Int, error) {
|
|
// If the current block is the first EIP-1559 block, or it is the genesis block
|
|
// return the initial slice and initial base fee.
|
|
if !config.IsEVM(parent.Time) || parent.Number.Cmp(common.Big0) == 0 {
|
|
return big.NewInt(feeConfig.MinBaseFee.Int64()), nil
|
|
}
|
|
|
|
dynamicFeeWindow, err := feeWindow(config, parent, timestamp)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Calculate the amount of gas consumed within the rollup window.
|
|
var (
|
|
baseFee = new(big.Int).Set(parent.BaseFee)
|
|
baseFeeChangeDenominator = feeConfig.BaseFeeChangeDenominator
|
|
parentGasTarget = feeConfig.TargetGas.Uint64()
|
|
totalGas = dynamicFeeWindow.Sum()
|
|
)
|
|
|
|
if totalGas == parentGasTarget {
|
|
// If the parent block used exactly its target gas, the baseFee stays
|
|
// the same.
|
|
//
|
|
// For legacy reasons, this is true even if the baseFee would have
|
|
// otherwise been clamped to a different range.
|
|
return baseFee, nil
|
|
}
|
|
|
|
var (
|
|
num = new(big.Int)
|
|
parentGasTargetBig = new(big.Int).SetUint64(parentGasTarget)
|
|
)
|
|
if totalGas > parentGasTarget {
|
|
// If the parent block used more gas than its target, the baseFee should increase.
|
|
num.SetUint64(totalGas - parentGasTarget)
|
|
num.Mul(num, parent.BaseFee)
|
|
num.Div(num, parentGasTargetBig)
|
|
num.Div(num, baseFeeChangeDenominator)
|
|
baseFeeDelta := bigMax(num, common.Big1)
|
|
|
|
baseFee.Add(baseFee, baseFeeDelta)
|
|
} else {
|
|
// Otherwise if the parent block used less gas than its target, the baseFee should decrease.
|
|
num.SetUint64(parentGasTarget - totalGas)
|
|
num.Mul(num, parent.BaseFee)
|
|
num.Div(num, parentGasTargetBig)
|
|
num.Div(num, baseFeeChangeDenominator)
|
|
baseFeeDelta := bigMax(num, common.Big1)
|
|
|
|
if timestamp < parent.Time {
|
|
// This should never happen as the fee window calculations should
|
|
// have already failed, but it is kept for clarity.
|
|
return nil, fmt.Errorf("cannot calculate base fee for timestamp %d prior to parent timestamp %d",
|
|
timestamp,
|
|
parent.Time,
|
|
)
|
|
}
|
|
|
|
// If timeElapsed is greater than [feewindow.WindowLen], apply the state
|
|
// transition to the base fee to account for the interval during which
|
|
// no blocks were produced.
|
|
//
|
|
// We use timeElapsed/[feewindow.WindowLen], so that the transition is applied
|
|
// for every [feewindow.WindowLen] seconds that has elapsed between the parent
|
|
// and this block.
|
|
var (
|
|
timeElapsed = timestamp - parent.Time
|
|
windowsElapsed = timeElapsed / feewindow.WindowLen
|
|
)
|
|
if windowsElapsed > 1 {
|
|
bigWindowsElapsed := new(big.Int).SetUint64(windowsElapsed)
|
|
// Because baseFeeDelta could actually be [common.Big1], we must not
|
|
// modify the existing value of `baseFeeDelta` but instead allocate
|
|
// a new one.
|
|
baseFeeDelta = new(big.Int).Mul(baseFeeDelta, bigWindowsElapsed)
|
|
}
|
|
baseFee.Sub(baseFee, baseFeeDelta)
|
|
}
|
|
|
|
// Ensure that the base fee does not increase/decrease outside of the bounds
|
|
baseFee = selectBigWithinBounds(feeConfig.MinBaseFee, baseFee, maxUint256)
|
|
|
|
return baseFee, nil
|
|
}
|
|
|
|
// feeWindow takes the previous header and the timestamp of its child block and
|
|
// calculates the expected fee window.
|
|
//
|
|
// feeWindow should only be called if timestamp >= config.EVMTimestamp
|
|
func feeWindow(
|
|
config *extras.ChainConfig,
|
|
parent *types.Header,
|
|
timestamp uint64,
|
|
) (feewindow.Window, error) {
|
|
// If the current block is the first EIP-1559 block, or it is the genesis block
|
|
// return the initial window.
|
|
if !config.IsEVM(parent.Time) || parent.Number.Cmp(common.Big0) == 0 {
|
|
return feewindow.Window{}, nil
|
|
}
|
|
|
|
dynamicFeeWindow, err := feewindow.ParseWindow(parent.Extra)
|
|
if err != nil {
|
|
return feewindow.Window{}, err
|
|
}
|
|
|
|
if timestamp < parent.Time {
|
|
return feewindow.Window{}, fmt.Errorf("%w: timestamp %d prior to parent timestamp %d",
|
|
errInvalidTimestamp,
|
|
timestamp,
|
|
parent.Time,
|
|
)
|
|
}
|
|
timeElapsed := timestamp - parent.Time
|
|
|
|
// Compute the new state of the gas rolling window.
|
|
dynamicFeeWindow.Add(parent.GasUsed)
|
|
|
|
// roll the window over by the timeElapsed to generate the new rollup
|
|
// window.
|
|
dynamicFeeWindow.Shift(timeElapsed)
|
|
return dynamicFeeWindow, nil
|
|
}
|
|
|
|
// selectBigWithinBounds returns [value] if it is within the bounds:
|
|
// lowerBound <= value <= upperBound or the bound at either end if [value]
|
|
// is outside of the defined boundaries.
|
|
func selectBigWithinBounds(lowerBound, value, upperBound *big.Int) *big.Int {
|
|
switch {
|
|
case lowerBound != nil && value.Cmp(lowerBound) < 0:
|
|
return new(big.Int).Set(lowerBound)
|
|
case upperBound != nil && value.Cmp(upperBound) > 0:
|
|
return new(big.Int).Set(upperBound)
|
|
default:
|
|
return value
|
|
}
|
|
}
|