github.com/ava-labs/avalanchego@v1.11.11/vms/components/gas/gas.go (about) 1 // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. 2 // See the file LICENSE for licensing terms. 3 4 package gas 5 6 import ( 7 "math" 8 9 "github.com/holiman/uint256" 10 11 safemath "github.com/ava-labs/avalanchego/utils/math" 12 ) 13 14 var maxUint64 = new(uint256.Int).SetUint64(math.MaxUint64) 15 16 type ( 17 Gas uint64 18 Price uint64 19 ) 20 21 // Cost converts the gas to nAVAX based on the price. 22 // 23 // If overflow would occur, an error is returned. 24 func (g Gas) Cost(price Price) (uint64, error) { 25 return safemath.Mul(uint64(g), uint64(price)) 26 } 27 28 // AddPerSecond returns g + gasPerSecond * seconds. 29 // 30 // If overflow would occur, MaxUint64 is returned. 31 func (g Gas) AddPerSecond(gasPerSecond Gas, seconds uint64) Gas { 32 newGas, err := safemath.Mul(uint64(gasPerSecond), seconds) 33 if err != nil { 34 return math.MaxUint64 35 } 36 totalGas, err := safemath.Add(uint64(g), newGas) 37 if err != nil { 38 return math.MaxUint64 39 } 40 return Gas(totalGas) 41 } 42 43 // SubPerSecond returns g - gasPerSecond * seconds. 44 // 45 // If underflow would occur, 0 is returned. 46 func (g Gas) SubPerSecond(gasPerSecond Gas, seconds uint64) Gas { 47 gasToRemove, err := safemath.Mul(uint64(gasPerSecond), seconds) 48 if err != nil { 49 return 0 50 } 51 totalGas, err := safemath.Sub(uint64(g), gasToRemove) 52 if err != nil { 53 return 0 54 } 55 return Gas(totalGas) 56 } 57 58 // CalculatePrice returns the gas price given the minimum gas price, the 59 // excess gas, and the excess conversion constant. 60 // 61 // It is defined as an approximation of: 62 // 63 // minPrice * e^(excess / excessConversionConstant) 64 // 65 // This implements the EIP-4844 fake exponential formula: 66 // 67 // def fake_exponential(factor: int, numerator: int, denominator: int) -> int: 68 // i = 1 69 // output = 0 70 // numerator_accum = factor * denominator 71 // while numerator_accum > 0: 72 // output += numerator_accum 73 // numerator_accum = (numerator_accum * numerator) // (denominator * i) 74 // i += 1 75 // return output // denominator 76 // 77 // This implementation is optimized with the knowledge that any value greater 78 // than MaxUint64 gets returned as MaxUint64. This means that every intermediate 79 // value is guaranteed to be at most MaxUint193. So, we can safely use 80 // uint256.Int. 81 // 82 // This function does not perform any memory allocations. 83 // 84 //nolint:dupword // The python is copied from the EIP-4844 specification 85 func CalculatePrice( 86 minPrice Price, 87 excess Gas, 88 excessConversionConstant Gas, 89 ) Price { 90 var ( 91 numerator uint256.Int 92 denominator uint256.Int 93 94 i uint256.Int 95 output uint256.Int 96 numeratorAccum uint256.Int 97 98 maxOutput uint256.Int 99 ) 100 numerator.SetUint64(uint64(excess)) // range is [0, MaxUint64] 101 denominator.SetUint64(uint64(excessConversionConstant)) // range is [0, MaxUint64] 102 103 i.SetOne() 104 numeratorAccum.SetUint64(uint64(minPrice)) // range is [0, MaxUint64] 105 numeratorAccum.Mul(&numeratorAccum, &denominator) // range is [0, MaxUint128] 106 107 maxOutput.Mul(&denominator, maxUint64) // range is [0, MaxUint128] 108 for numeratorAccum.Sign() > 0 { 109 output.Add(&output, &numeratorAccum) // range is [0, MaxUint192+MaxUint128] 110 if output.Cmp(&maxOutput) >= 0 { 111 return math.MaxUint64 112 } 113 // maxOutput < MaxUint128 so numeratorAccum < MaxUint128. 114 numeratorAccum.Mul(&numeratorAccum, &numerator) // range is [0, MaxUint192] 115 numeratorAccum.Div(&numeratorAccum, &denominator) 116 numeratorAccum.Div(&numeratorAccum, &i) 117 118 i.AddUint64(&i, 1) 119 } 120 return Price(output.Div(&output, &denominator).Uint64()) 121 }