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  }