Skip to content

Commit 70d12c3

Browse files
committed
move fake expo calculation to safemath
1 parent b20dfd9 commit 70d12c3

File tree

3 files changed

+82
-64
lines changed

3 files changed

+82
-64
lines changed

utils/math/exponential.go

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
// Copyright (C) 2019-2025, Ava Labs, Inc. All rights reserved.
2+
// See the file LICENSE for licensing terms.
3+
4+
package math
5+
6+
import (
7+
"math"
8+
9+
"github.com/holiman/uint256"
10+
)
11+
12+
var max256Uint64 = new(uint256.Int).SetUint64(math.MaxUint64)
13+
14+
// CalculateExponential returns the approximate exponential result given the factor, the
15+
// numerator, and the denominator.
16+
//
17+
// It is defined as an approximation of:
18+
//
19+
// factor * e^(numerator / denominator)
20+
//
21+
// This implements the EIP-4844 fake exponential formula:
22+
//
23+
// def fake_exponential(factor: int, numerator: int, denominator: int) -> int:
24+
// i = 1
25+
// output = 0
26+
// numerator_accum = factor * denominator
27+
// while numerator_accum > 0:
28+
// output += numerator_accum
29+
// numerator_accum = (numerator_accum * numerator) // (denominator * i)
30+
// i += 1
31+
// return output // denominator
32+
//
33+
// This implementation is optimized with the knowledge that any value greater
34+
// than MaxUint64 gets returned as MaxUint64. This means that every intermediate
35+
// value is guaranteed to be at most MaxUint193. So, we can safely use
36+
// uint256.Int.
37+
//
38+
// This function does not perform any memory allocations.
39+
//
40+
//nolint:dupword // The python is copied from the EIP-4844 specification
41+
func CalculateExponential(
42+
factor uint64,
43+
numerator uint64,
44+
denominator uint64,
45+
) uint64 {
46+
var (
47+
num uint256.Int
48+
denom uint256.Int
49+
50+
i uint256.Int
51+
output uint256.Int
52+
numeratorAccum uint256.Int
53+
54+
maxOutput uint256.Int
55+
)
56+
num.SetUint64(uint64(numerator)) // range is [0, MaxUint64]
57+
denom.SetUint64(uint64(denominator)) // range is [0, MaxUint64]
58+
59+
i.SetOne()
60+
numeratorAccum.SetUint64(uint64(factor)) // range is [0, MaxUint64]
61+
numeratorAccum.Mul(&numeratorAccum, &denom) // range is [0, MaxUint128]
62+
63+
maxOutput.Mul(&denom, max256Uint64) // range is [0, MaxUint128]
64+
for numeratorAccum.Sign() > 0 {
65+
output.Add(&output, &numeratorAccum) // range is [0, MaxUint192+MaxUint128]
66+
if output.Cmp(&maxOutput) >= 0 {
67+
return math.MaxUint64
68+
}
69+
// maxOutput < MaxUint128 so numeratorAccum < MaxUint128.
70+
numeratorAccum.Mul(&numeratorAccum, &num) // range is [0, MaxUint192]
71+
numeratorAccum.Div(&numeratorAccum, &denom)
72+
numeratorAccum.Div(&numeratorAccum, &i)
73+
74+
i.AddUint64(&i, 1)
75+
}
76+
return output.Div(&output, &denom).Uint64()
77+
}

vms/components/gas/gas.go

Lines changed: 1 addition & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,9 @@ package gas
66
import (
77
"math"
88

9-
"github.com/holiman/uint256"
10-
119
safemath "github.com/ava-labs/avalanchego/utils/math"
1210
)
1311

14-
var maxUint64 = new(uint256.Int).SetUint64(math.MaxUint64)
15-
1612
type (
1713
Gas uint64
1814
Price uint64
@@ -57,65 +53,10 @@ func (g Gas) SubOverTime(gasRate Gas, duration uint64) Gas {
5753

5854
// CalculatePrice returns the gas price given the minimum gas price, the
5955
// 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
8556
func CalculatePrice(
8657
minPrice Price,
8758
excess Gas,
8859
excessConversionConstant Gas,
8960
) 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())
61+
return Price(safemath.CalculateExponential(uint64(minPrice), uint64(excess), uint64(excessConversionConstant)))
12162
}

vms/evm/upgrades/common/target_excess.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,10 @@ func (p TargetExcessParams) DesiredTargetExcess(desiredTarget uint64) uint64 {
4646
// CalculateTarget calculates the target value using exponential formula:
4747
// Target = MinTarget * e^(Excess / TargetConversion)
4848
func (p TargetExcessParams) CalculateTarget(excess uint64) uint64 {
49-
return uint64(gas.CalculatePrice(
50-
gas.Price(p.MinTarget),
51-
gas.Gas(excess),
52-
gas.Gas(p.TargetConversion),
49+
return uint64(safemath.CalculateExponential(
50+
p.MinTarget,
51+
excess,
52+
p.TargetConversion,
5353
))
5454
}
5555

0 commit comments

Comments
 (0)