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 }