Skip to content

Commit 8ec77cd

Browse files
Refactor and improve multi-gas price calculations
1 parent 4f2c34e commit 8ec77cd

File tree

8 files changed

+275
-114
lines changed

8 files changed

+275
-114
lines changed

arbos/constraints/multi_dimensional.go

Lines changed: 18 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -93,50 +93,6 @@ func (c *MultiGasConstraint) SetResourceWeights(weights map[uint8]uint64) error
9393
return c.sumWeights.Set(total)
9494
}
9595

96-
// ComputeExponent returns the exponent of the given constraint for the given resource kind.
97-
func (c *MultiGasConstraint) ComputeExponent(kind uint8) (arbmath.Bips, error) {
98-
if _, err := multigas.CheckResourceKind(kind); err != nil {
99-
return 0, err
100-
}
101-
102-
weight, err := c.weightedResources[int(kind)].Get()
103-
if err != nil {
104-
return 0, err
105-
}
106-
if weight == 0 {
107-
return 0, nil
108-
}
109-
110-
backlog, err := c.backlog.Get()
111-
if err != nil || backlog == 0 {
112-
return 0, err
113-
}
114-
115-
target, err := c.target.Get()
116-
if err != nil || target == 0 {
117-
return 0, err
118-
}
119-
120-
adjustmentWindow, err := c.adjustmentWindow.Get()
121-
if err != nil || adjustmentWindow == 0 {
122-
return 0, err
123-
}
124-
125-
sumWeights, err := c.sumWeights.Get()
126-
if err != nil || sumWeights == 0 {
127-
return 0, err
128-
}
129-
130-
dividend := arbmath.NaturalToBips(
131-
arbmath.SaturatingCast[int64](arbmath.SaturatingUMul(backlog, weight)))
132-
divisor := arbmath.SaturatingCastToBips(
133-
arbmath.SaturatingUMul(uint64(adjustmentWindow),
134-
arbmath.SaturatingUMul(target, sumWeights)))
135-
exponent := dividend / divisor
136-
137-
return exponent, nil
138-
}
139-
14096
// IncrementBacklog increments the constraint backlog based on multi-dimensional gas usage
14197
func (c *MultiGasConstraint) IncrementBacklog(multiGas multigas.MultiGas) error {
14298
totalBacklog, err := c.backlog.Get()
@@ -218,6 +174,10 @@ func (c *MultiGasConstraint) ResourceWeight(kind uint8) (uint64, error) {
218174
return c.weightedResources[kind].Get()
219175
}
220176

177+
func (c *MultiGasConstraint) SumWeights() (uint64, error) {
178+
return c.sumWeights.Get()
179+
}
180+
221181
func (c *MultiGasConstraint) ResourcesWithWeights() (map[multigas.ResourceKind]uint64, error) {
222182
result := make(map[multigas.ResourceKind]uint64)
223183
for i := range uint8(multigas.NumResourceKind) {
@@ -231,3 +191,17 @@ func (c *MultiGasConstraint) ResourcesWithWeights() (map[multigas.ResourceKind]u
231191
}
232192
return result, nil
233193
}
194+
195+
func (c *MultiGasConstraint) UsedResources() ([]multigas.ResourceKind, error) {
196+
var result []multigas.ResourceKind
197+
for i := range uint8(multigas.NumResourceKind) {
198+
weight, err := c.weightedResources[i].Get()
199+
if err != nil {
200+
return nil, err
201+
}
202+
if weight != 0 {
203+
result = append(result, multigas.ResourceKind(i))
204+
}
205+
}
206+
return result, nil
207+
}

arbos/constraints/multi_dimensional_test.go

Lines changed: 21 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import (
99

1010
"github.com/offchainlabs/nitro/arbos/burn"
1111
"github.com/offchainlabs/nitro/arbos/storage"
12-
"github.com/offchainlabs/nitro/util/arbmath"
1312
)
1413

1514
func TestMultiGasConstraint(t *testing.T) {
@@ -27,11 +26,11 @@ func TestMultiGasConstraint(t *testing.T) {
2726
require.Equal(t, uint32(456), window)
2827
require.Equal(t, uint64(789), backlog)
2928

30-
weights := map[uint8]uint64{
29+
weightedResources := map[uint8]uint64{
3130
uint8(multigas.ResourceKindComputation): 10,
3231
uint8(multigas.ResourceKindStorageAccess): 20,
3332
}
34-
require.NoError(t, c.SetResourceWeights(weights))
33+
require.NoError(t, c.SetResourceWeights(weightedResources))
3534

3635
w1, _ := c.ResourceWeight(uint8(multigas.ResourceKindComputation))
3736
w2, _ := c.ResourceWeight(uint8(multigas.ResourceKindStorageAccess))
@@ -44,13 +43,31 @@ func TestMultiGasConstraint(t *testing.T) {
4443
require.Equal(t, uint64(10), res[multigas.ResourceKindComputation])
4544
require.Equal(t, uint64(20), res[multigas.ResourceKindStorageAccess])
4645

46+
used, err := c.UsedResources()
47+
require.NoError(t, err)
48+
require.Len(t, used, 2)
49+
require.Contains(t, used, multigas.ResourceKindComputation)
50+
require.Contains(t, used, multigas.ResourceKindStorageAccess)
51+
52+
weights, err := c.SumWeights()
53+
require.NoError(t, err)
54+
require.Equal(t, uint64(30), weights)
55+
4756
require.NoError(t, c.Clear())
4857
target, _ = c.Target()
4958
backlog, _ = c.Backlog()
5059
require.Zero(t, target)
5160
require.Zero(t, backlog)
5261
res, _ = c.ResourcesWithWeights()
5362
require.Empty(t, res)
63+
64+
used, err = c.UsedResources()
65+
require.NoError(t, err)
66+
require.Len(t, used, 0)
67+
68+
weights, err = c.SumWeights()
69+
require.NoError(t, err)
70+
require.Equal(t, uint64(0), weights)
5471
}
5572

5673
func TestMultiGasConstraintResourceWeightsValidation(t *testing.T) {
@@ -75,7 +92,7 @@ func TestMultiGasConstraintResourceWeightsValidation(t *testing.T) {
7592
require.Equal(t, uint64(10), total)
7693
}
7794

78-
func TestMultiGasConstraintBacklogAggregationAndComputeExponent(t *testing.T) {
95+
func TestMultiGasConstraintBacklogAggregation(t *testing.T) {
7996
sto := storage.NewMemoryBacked(burn.NewSystemBurner(nil, false))
8097
c := OpenMultiGasConstraint(sto)
8198

@@ -93,25 +110,6 @@ func TestMultiGasConstraintBacklogAggregationAndComputeExponent(t *testing.T) {
93110
)
94111

95112
require.NoError(t, c.IncrementBacklog(mg))
96-
97-
backlog, err := c.Backlog()
98-
require.NoError(t, err)
99-
require.Equal(t, uint64(30), backlog) // 1*10 + 2*10 = 30
100-
101-
compExp, err := c.ComputeExponent(uint8(multigas.ResourceKindComputation))
102-
require.NoError(t, err)
103-
storExp, err := c.ComputeExponent(uint8(multigas.ResourceKindStorageAccess))
104-
require.NoError(t, err)
105-
106-
// expected: backlog * weight / (A * T * sumWeights)
107-
// backlog=30, target=5, window=2, sumWeights=3
108-
// computation: (30*1)/(2*5*3) = 1
109-
// storage: (30*2)/(2*5*3) = 2
110-
require.Equal(t, arbmath.Bips(10000), compExp)
111-
require.Equal(t, arbmath.Bips(20000), storExp)
112-
113-
// ratio must reflect weights (1:2)
114-
require.Equal(t, 2*compExp, storExp)
115113
}
116114

117115
func TestMultiGasConstraintBacklogGrowth(t *testing.T) {

arbos/l2pricing/l2pricing.go

Lines changed: 57 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,11 @@ package l2pricing
55

66
import (
77
"fmt"
8+
"math"
89
"math/big"
910

11+
"github.com/ethereum/go-ethereum/arbitrum/multigas"
12+
1013
"github.com/offchainlabs/nitro/arbos/constraints"
1114
"github.com/offchainlabs/nitro/arbos/storage"
1215
"github.com/offchainlabs/nitro/util/arbmath"
@@ -41,7 +44,7 @@ var gasConstraintsKey []byte = []byte{0}
4144
var multigasConstraintsKey []byte = []byte{1}
4245

4346
const GethBlockGasLimit = 1 << 50
44-
const MaxExponentBips = arbmath.Bips(85_000)
47+
const MaxPricingExponentBips = arbmath.Bips(85_000)
4548

4649
func InitializeL2PricingState(sto *storage.Storage) error {
4750
_ = sto.SetUint64ByUint64(speedLimitPerSecondOffset, InitialSpeedLimitPerSecondV0)
@@ -164,6 +167,59 @@ func (ps *L2PricingState) setGasConstraintsFromLegacy() error {
164167
return ps.AddGasConstraint(target, adjustmentWindow, backlog)
165168
}
166169

170+
func (ps *L2PricingState) setMultiGasConstraintsFromSingleGasConstraints() error {
171+
if err := ps.ClearMultiGasConstraints(); err != nil {
172+
return err
173+
}
174+
175+
length, err := ps.GasConstraintsLength()
176+
if err != nil {
177+
return err
178+
}
179+
180+
for i := range length {
181+
c := ps.OpenGasConstraintAt(i)
182+
183+
target, err := c.Target()
184+
if err != nil {
185+
return fmt.Errorf("failed to read target from constraint %d: %w", i, err)
186+
}
187+
window, err := c.AdjustmentWindow()
188+
if err != nil {
189+
return fmt.Errorf("failed to read adjustment window from constraint %d: %w", i, err)
190+
}
191+
backlog, err := c.Backlog()
192+
if err != nil {
193+
return fmt.Errorf("failed to read backlog from constraint %d: %w", i, err)
194+
}
195+
196+
// NOTE: this code kept for pricing algorithm without wight normalization
197+
//
198+
// resourceWeights := make(map[uint8]uint64, len(FeeRelevantResourceKinds))
199+
// for _, kind := range FeeRelevantResourceKinds {
200+
// resourceWeights[uint8(kind)] = 1
201+
// }
202+
resourceWeights := map[uint8]uint64{uint8(multigas.ResourceKindComputation): 1}
203+
204+
var uint32Window uint32
205+
if window > math.MaxUint32 {
206+
uint32Window = math.MaxUint32
207+
} else {
208+
uint32Window = uint32(window)
209+
}
210+
211+
if err := ps.AddMultiGasConstraint(
212+
target,
213+
uint32Window,
214+
backlog,
215+
resourceWeights,
216+
); err != nil {
217+
return fmt.Errorf("failed to add multi-gas constraint %d: %w", i, err)
218+
}
219+
}
220+
return nil
221+
}
222+
167223
func (ps *L2PricingState) AddGasConstraint(target uint64, adjustmentWindow uint64, backlog uint64) error {
168224
subStorage, err := ps.gasConstraints.Push()
169225
if err != nil {
@@ -240,17 +296,6 @@ func (ps *L2PricingState) AddMultiGasConstraint(
240296
if err := constraint.SetResourceWeights(resourceWeights); err != nil {
241297
return fmt.Errorf("failed to set resource weights: %w", err)
242298
}
243-
244-
for kind := range resourceWeights {
245-
exp, err := constraint.ComputeExponent(kind)
246-
if err != nil {
247-
return fmt.Errorf("failed to compute exponent for resource kind %v: %w", kind, err)
248-
}
249-
if exp > MaxExponentBips {
250-
return fmt.Errorf("resource kind %v has exponent %v bips exceeding max of %v bips", kind, exp, MaxExponentBips)
251-
}
252-
}
253-
254299
return nil
255300
}
256301

arbos/l2pricing/l2pricing_test.go

Lines changed: 25 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -221,33 +221,42 @@ func TestMultiGasConstraints(t *testing.T) {
221221
}
222222
}
223223

224-
func TestMultiGasConstraintExponentSafety(t *testing.T) {
224+
func TestMultiGasConstraintsExponents(t *testing.T) {
225225
pricing := PricingForTest(t)
226226

227-
weights := map[uint8]uint64{
228-
uint8(multigas.ResourceKindComputation): 1,
229-
}
230-
// backlog=100, target=100, A=10 -> exponent = (100*1)/(10*100*1) = 0.01 -> safe
227+
// backlog=100, target=100, A=10 -> exponent = (100*1)/(10*100*1) = 1000 bips
231228
err := pricing.AddMultiGasConstraint(
232229
100,
233230
10,
234231
100,
235-
weights,
232+
map[uint8]uint64{
233+
uint8(multigas.ResourceKindComputation): 1,
234+
},
236235
)
237-
if err != nil {
238-
t.Fatalf("expected valid constraint, got error: %v", err)
239-
}
236+
Require(t, err)
240237

241-
// backlog very large -> exponent blows past MaxExponentBips
238+
// backlog=200, target=40, A=20 -> exponent = (200*1)/(20*40*1) = 2500 bips
242239
err = pricing.AddMultiGasConstraint(
243-
1,
244-
1,
245-
1_000_000_000_000,
246-
weights,
240+
40,
241+
20,
242+
200,
243+
map[uint8]uint64{
244+
uint8(multigas.ResourceKindStorageAccess): 2,
245+
},
247246
)
247+
Require(t, err)
248+
249+
exps, err := pricing.CalcMultiGasConstraintsExponents()
250+
Require(t, err)
251+
252+
expected := arbmath.Bips(1000)
253+
if exps[multigas.ResourceKindComputation] != expected {
254+
t.Fatalf("wrong exponent: got %v, want %v", exps[multigas.ResourceKindComputation], expected)
255+
}
248256

249-
if err == nil {
250-
t.Fatalf("expected AddMultiGasConstraint to reject unsafe exponent, but got no error")
257+
expected = arbmath.Bips(2500)
258+
if exps[multigas.ResourceKindStorageAccess] != expected {
259+
t.Fatalf("wrong exponent: got %v, want %v", exps[multigas.ResourceKindStorageAccess], expected)
251260
}
252261
}
253262

0 commit comments

Comments
 (0)