Skip to content

Commit e2bfb52

Browse files
Finalise resource management precomiles implementation
1 parent 93bf31d commit e2bfb52

File tree

9 files changed

+170
-91
lines changed

9 files changed

+170
-91
lines changed

arbos/constraints/constraints.go

Lines changed: 41 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,35 +16,65 @@ import (
1616
// Going over the target for this given period will increase the gas price.
1717
type PeriodSecs uint32
1818

19+
// ResourceWeight is a multiplier applied to a resource’s gas usage when computing backlog.
1920
type ResourceWeight uint64
2021

21-
// ResourceSet tracks resource weights for constraint calculation.
22-
type ResourceSet struct {
22+
// WeightedResourceSet tracks resource weights for constraint calculation.
23+
type WeightedResourceSet struct {
2324
weights [multigas.NumResourceKind]ResourceWeight
2425
}
2526

27+
// ResourceSet represents presence of each resource kind.
28+
type ResourceSet struct {
29+
kinds [multigas.NumResourceKind]bool
30+
}
31+
2632
const MaxResourceWeight = 1_000_000 // 1e6
2733

28-
// EmptyResourceSet creates a new set with all weights initialized to zero.
29-
func EmptyResourceSet() ResourceSet {
30-
return ResourceSet{
34+
// NewWeightedResourceSet creates a new weighted set with all weights initialized to zero.
35+
func NewWeightedResourceSet() WeightedResourceSet {
36+
return WeightedResourceSet{
3137
weights: [multigas.NumResourceKind]ResourceWeight{},
3238
}
3339
}
3440

3541
// WithResource sets the weight for a single resource.
36-
func (s ResourceSet) WithResource(resource multigas.ResourceKind, weight ResourceWeight) ResourceSet {
42+
func (s WeightedResourceSet) WithResource(resource multigas.ResourceKind, weight ResourceWeight) WeightedResourceSet {
3743
s.weights[resource] = weight
3844
return s
3945
}
4046

4147
// HasResource returns true if the resource has a non-zero weight in the set.
42-
func (s ResourceSet) HasResource(resource multigas.ResourceKind) bool {
48+
func (s WeightedResourceSet) HasResource(resource multigas.ResourceKind) bool {
4349
return s.weights[resource] != 0
4450
}
4551

52+
// WithoutWeights returns resources set without weights
53+
func (s WeightedResourceSet) WithoutWeights() ResourceSet {
54+
var rs ResourceSet
55+
for i, weight := range s.weights {
56+
if weight > 0 {
57+
rs.kinds[i] = true
58+
}
59+
}
60+
return rs
61+
}
62+
63+
// EmptyResourceSet creates a new empty resource set.
64+
func EmptyResourceSet() ResourceSet {
65+
return ResourceSet{
66+
kinds: [multigas.NumResourceKind]bool{},
67+
}
68+
}
69+
70+
// WithResource returns a copy of the set with the given resource weight updated.
71+
func (s ResourceSet) WithResource(resource multigas.ResourceKind) ResourceSet {
72+
s.kinds[resource] = true
73+
return s
74+
}
75+
4676
// All returns all resources with non-zero weights.
47-
func (s ResourceSet) All() iter.Seq2[multigas.ResourceKind, ResourceWeight] {
77+
func (s WeightedResourceSet) All() iter.Seq2[multigas.ResourceKind, ResourceWeight] {
4878
return func(yield func(multigas.ResourceKind, ResourceWeight) bool) {
4979
for i, weight := range s.weights {
5080
if weight != 0 {
@@ -60,7 +90,7 @@ func (s ResourceSet) All() iter.Seq2[multigas.ResourceKind, ResourceWeight] {
6090

6191
// ResourceConstraint defines the max gas target per second for the given period for a single resource.
6292
type ResourceConstraint struct {
63-
Resources ResourceSet
93+
Resources WeightedResourceSet
6494
Period PeriodSecs
6595
TargetPerSec uint64
6696
Backlog uint64
@@ -114,10 +144,10 @@ func NewResourceConstraints() *ResourceConstraints {
114144
// Set adds or updates the given resource constraint.
115145
// The set of resources and the period are the key that defines the constraint.
116146
func (rc *ResourceConstraints) Set(
117-
resources ResourceSet, periodSecs PeriodSecs, targetPerSec uint64,
147+
resources WeightedResourceSet, periodSecs PeriodSecs, targetPerSec uint64,
118148
) {
119149
key := constraintKey{
120-
resources: resources,
150+
resources: resources.WithoutWeights(),
121151
period: periodSecs,
122152
}
123153
constraint := &ResourceConstraint{

arbos/constraints/constraints_test.go

Lines changed: 21 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import (
1313
)
1414

1515
func TestResourceSetWithResource(t *testing.T) {
16-
s := EmptyResourceSet().
16+
s := NewWeightedResourceSet().
1717
WithResource(multigas.ResourceKindComputation, 1).
1818
WithResource(multigas.ResourceKindStorageAccess, 2)
1919
for resource, weight := range s.All() {
@@ -23,7 +23,7 @@ func TestResourceSetWithResource(t *testing.T) {
2323
}
2424

2525
func TestResourceSetGetResources(t *testing.T) {
26-
s := EmptyResourceSet().
26+
s := NewWeightedResourceSet().
2727
WithResource(multigas.ResourceKindComputation, 1).
2828
WithResource(multigas.ResourceKindStorageAccess, 1)
2929

@@ -41,20 +41,22 @@ func TestResourceSetGetResources(t *testing.T) {
4141
}
4242

4343
func TestOverrideResourceWeights(t *testing.T) {
44-
s := EmptyResourceSet().
44+
s := NewWeightedResourceSet().
4545
WithResource(multigas.ResourceKindComputation, 1).
4646
WithResource(multigas.ResourceKindStorageAccess, 2)
4747
require.Equal(t, ResourceWeight(1), s.weights[multigas.ResourceKindComputation])
4848
require.Equal(t, ResourceWeight(2), s.weights[multigas.ResourceKindStorageAccess])
49+
used := s.WithoutWeights()
4950

5051
s = s.WithResource(multigas.ResourceKindComputation, 3).
51-
WithResource(multigas.ResourceKindStorageAccess, 0)
52+
WithResource(multigas.ResourceKindStorageAccess, 10)
5253
require.Equal(t, ResourceWeight(3), s.weights[multigas.ResourceKindComputation])
53-
require.False(t, s.HasResource(multigas.ResourceKindStorageAccess))
54+
require.Equal(t, ResourceWeight(10), s.weights[multigas.ResourceKindStorageAccess])
55+
require.Equal(t, used, s.WithoutWeights())
5456
}
5557

5658
func TestAddToBacklog(t *testing.T) {
57-
resources := EmptyResourceSet().
59+
resources := NewWeightedResourceSet().
5860
WithResource(multigas.ResourceKindComputation, 1).
5961
WithResource(multigas.ResourceKindStorageAccess, 1)
6062
c := &ResourceConstraint{
@@ -78,7 +80,7 @@ func TestAddToBacklog(t *testing.T) {
7880
}
7981

8082
func TestAddToBacklogWithWeights(t *testing.T) {
81-
resources := EmptyResourceSet().
83+
resources := NewWeightedResourceSet().
8284
WithResource(multigas.ResourceKindComputation, 2).
8385
WithResource(multigas.ResourceKindStorageAccess, 3)
8486
c := &ResourceConstraint{
@@ -125,14 +127,14 @@ func TestNewResourceConstraints(t *testing.T) {
125127

126128
func TestSetResourceConstraints(t *testing.T) {
127129
rc := NewResourceConstraints()
128-
resources := EmptyResourceSet().
130+
resources := NewWeightedResourceSet().
129131
WithResource(multigas.ResourceKindComputation, 1)
130132
periodSecs := PeriodSecs(10)
131133
targetPerSec := uint64(100)
132134

133135
rc.Set(resources, periodSecs, targetPerSec)
134136

135-
constraint := rc.Get(resources, periodSecs)
137+
constraint := rc.Get(resources.WithoutWeights(), periodSecs)
136138
require.NotNil(t, constraint)
137139
require.Equal(t, resources, constraint.Resources)
138140
require.Equal(t, periodSecs, constraint.Period)
@@ -141,57 +143,57 @@ func TestSetResourceConstraints(t *testing.T) {
141143

142144
func TestGetResourceConstraints(t *testing.T) {
143145
rc := NewResourceConstraints()
144-
resources := EmptyResourceSet().
146+
resources := NewWeightedResourceSet().
145147
WithResource(multigas.ResourceKindComputation, 1)
146148
periodSecs := PeriodSecs(10)
147149
targetPerSec := uint64(100)
148150

149151
rc.Set(resources, periodSecs, targetPerSec)
150152

151153
// Test getting an existing constraint
152-
constraint := rc.Get(resources, periodSecs)
154+
constraint := rc.Get(resources.WithoutWeights(), periodSecs)
153155
require.NotNil(t, constraint)
154156
require.Equal(t, resources, constraint.Resources)
155157
require.Equal(t, periodSecs, constraint.Period)
156158
require.Equal(t, targetPerSec, constraint.TargetPerSec)
157159
require.Equal(t, uint64(0), constraint.Backlog)
158160

159161
// Test getting a non-existent constraint
160-
nonExistentResources := EmptyResourceSet().
162+
nonExistentResources := NewWeightedResourceSet().
161163
WithResource(multigas.ResourceKindStorageAccess, 1)
162-
constraint = rc.Get(nonExistentResources, periodSecs)
164+
constraint = rc.Get(nonExistentResources.WithoutWeights(), periodSecs)
163165
require.Nil(t, constraint)
164166
}
165167

166168
func TestClearResourceConstraints(t *testing.T) {
167169
rc := NewResourceConstraints()
168-
resources := EmptyResourceSet().
170+
resources := NewWeightedResourceSet().
169171
WithResource(multigas.ResourceKindComputation, 1)
170172
periodSecs := PeriodSecs(10)
171173
targetPerSec := uint64(100)
172174

173175
rc.Set(resources, periodSecs, targetPerSec)
174176

175177
// Ensure the constraint was set
176-
constraint := rc.Get(resources, periodSecs)
178+
constraint := rc.Get(resources.WithoutWeights(), periodSecs)
177179
require.NotNil(t, constraint)
178180

179181
// Clear the constraint
180-
rc.Clear(resources, periodSecs)
182+
rc.Clear(resources.WithoutWeights(), periodSecs)
181183

182184
// Ensure the constraint is gone
183-
constraint = rc.Get(resources, periodSecs)
185+
constraint = rc.Get(resources.WithoutWeights(), periodSecs)
184186
require.Nil(t, constraint)
185187
}
186188

187189
func TestAllResourceConstraints(t *testing.T) {
188190
rc := NewResourceConstraints()
189-
resources1 := EmptyResourceSet().
191+
resources1 := NewWeightedResourceSet().
190192
WithResource(multigas.ResourceKindComputation, 1)
191193
periodSecs1 := PeriodSecs(10)
192194
targetPerSec1 := uint64(100)
193195

194-
resources2 := EmptyResourceSet().
196+
resources2 := NewWeightedResourceSet().
195197
WithResource(multigas.ResourceKindStorageAccess, 1)
196198
periodSecs2 := PeriodSecs(20)
197199
targetPerSec2 := uint64(200)

arbos/constraints/storage.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ func (src *StorageResourceConstraints) Load() (*ResourceConstraints, error) {
100100
rc := NewResourceConstraints()
101101
for _, c := range payload.Constraints {
102102
rc.Set(c.Resources, c.Period, c.TargetPerSec)
103-
ptr := rc.Get(c.Resources, c.Period)
103+
ptr := rc.Get(c.Resources.WithoutWeights(), c.Period)
104104
ptr.Backlog = c.Backlog
105105
}
106106
return rc, nil
@@ -131,7 +131,7 @@ func (src *StorageResourceConstraints) Write(rc *ResourceConstraints) error {
131131
}
132132

133133
// SetConstraint adds or updates a resource constraint in storage.
134-
func (src *StorageResourceConstraints) SetConstraint(resources ResourceSet, periodSecs uint32, targetPerSec uint64) error {
134+
func (src *StorageResourceConstraints) SetConstraint(resources WeightedResourceSet, periodSecs uint32, targetPerSec uint64) error {
135135
rc, err := src.Load()
136136
if err != nil {
137137
return err

arbos/constraints/storage_test.go

Lines changed: 38 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,10 @@ func TestStorageResourceConstraintsRLPRoundTrip(t *testing.T) {
3434
name: "SimpleResourceConstraints",
3535
buildFunc: func() *ResourceConstraints {
3636
rc := NewResourceConstraints()
37-
res := EmptyResourceSet().
37+
res := NewWeightedResourceSet().
3838
WithResource(multigas.ResourceKindComputation, 1)
3939
rc.Set(res, PeriodSecs(10), 5_000_000)
40-
rc.Get(res, PeriodSecs(10)).Backlog = 12
40+
rc.Get(res.WithoutWeights(), PeriodSecs(10)).Backlog = 12
4141
return rc
4242
},
4343
},
@@ -46,17 +46,17 @@ func TestStorageResourceConstraintsRLPRoundTrip(t *testing.T) {
4646
buildFunc: func() *ResourceConstraints {
4747
rc := NewResourceConstraints()
4848

49-
res1 := EmptyResourceSet().
49+
res1 := NewWeightedResourceSet().
5050
WithResource(multigas.ResourceKindComputation, 1).
5151
WithResource(multigas.ResourceKindHistoryGrowth, 2)
5252
rc.Set(res1, PeriodSecs(12), 7_000_000)
53-
rc.Get(res1, PeriodSecs(12)).Backlog = 42
53+
rc.Get(res1.WithoutWeights(), PeriodSecs(12)).Backlog = 42
5454

55-
res2 := EmptyResourceSet().
55+
res2 := NewWeightedResourceSet().
5656
WithResource(multigas.ResourceKindWasmComputation, 3).
5757
WithResource(multigas.ResourceKindStorageGrowth, 5)
5858
rc.Set(res2, PeriodSecs(60), 3_500_000)
59-
rc.Get(res2, PeriodSecs(60)).Backlog = 99
59+
rc.Get(res2.WithoutWeights(), PeriodSecs(60)).Backlog = 99
6060

6161
return rc
6262
},
@@ -110,10 +110,10 @@ func TestStorageResourceConstraintsRLPRoundTrip(t *testing.T) {
110110

111111
func TestStorageResourceConstraintsRLPBacklogPersistence(t *testing.T) {
112112
rc := NewResourceConstraints()
113-
res := EmptyResourceSet().
113+
res := NewWeightedResourceSet().
114114
WithResource(multigas.ResourceKindComputation, 1)
115115
rc.Set(res, PeriodSecs(30), 10_000_000)
116-
ptr := rc.Get(res, PeriodSecs(30))
116+
ptr := rc.Get(res.WithoutWeights(), PeriodSecs(30))
117117
ptr.Backlog = 12345 // initial backlog value
118118

119119
store := &mockStorageBytes{}
@@ -153,3 +153,33 @@ func TestStorageResourceConstraintsEmptyLoad(t *testing.T) {
153153
require.NotNil(t, loaded, "Load() returned nil ResourceConstraints")
154154
require.Empty(t, loaded.constraints, "Loaded ResourceConstraints should be empty")
155155
}
156+
157+
func TestStorageDoesNotDependOnWeights(t *testing.T) {
158+
store := &mockStorageBytes{}
159+
src := NewStorageResourceConstraints(store)
160+
161+
res1 := NewWeightedResourceSet().
162+
WithResource(multigas.ResourceKindComputation, 1).
163+
WithResource(multigas.ResourceKindStorageAccess, 2)
164+
165+
err := src.SetConstraint(res1, 200, 10_000_000)
166+
require.NoError(t, err)
167+
168+
list, err := src.ListConstraints()
169+
require.NoError(t, err)
170+
require.Len(t, list, 1)
171+
require.Equal(t, res1, list[0].Resources)
172+
173+
res2 := NewWeightedResourceSet().
174+
WithResource(multigas.ResourceKindComputation, 50).
175+
WithResource(multigas.ResourceKindStorageAccess, 10)
176+
177+
// with same period should override existing entity
178+
err = src.SetConstraint(res2, 200, 1_000_000)
179+
require.NoError(t, err)
180+
181+
list, err = src.ListConstraints()
182+
require.NoError(t, err)
183+
require.Len(t, list, 1)
184+
require.Equal(t, res2, list[0].Resources)
185+
}

precompiles/ArbGasInfo.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -287,9 +287,9 @@ func (con ArbGasInfo) GetLastL1PricingSurplus(c ctx, evm mech) (*big.Int, error)
287287

288288
// Lists all resource constraints currently configured in ArbOS.
289289
func (con ArbGasInfo) ListResourceConstraints(c ctx, evm mech) ([]resourceConstraint, error) {
290-
constraints, err := c.State.ResourceConstraints().ListConstraints()
290+
rcs, err := c.State.ResourceConstraints().ListConstraints()
291291
if err != nil {
292292
return nil, err
293293
}
294-
return fromArbOsResourceConstraints(constraints), nil
294+
return fromArbOsResourceConstraints(rcs), nil
295295
}

precompiles/ArbOwner.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -456,15 +456,15 @@ func (con ArbOwner) SetCalldataPriceIncrease(c ctx, _ mech, enable bool) error {
456456

457457
// SetResourceConstraint adds or updates a resource constraint with specified resource kinds and weights
458458
func (con ArbOwner) SetResourceConstraint(c ctx, evm mech, resources []resourceWeight, periodSecs uint32, targetPerSec uint64) error {
459-
res, err := toArbOsResourceSet(resources)
459+
res, err := toArbOsWeightedResourceSet(resources)
460460
if err != nil {
461461
return err
462462
}
463463
return c.State.ResourceConstraints().SetConstraint(res, periodSecs, targetPerSec)
464464
}
465465

466466
// ClearConstraint removes a resource constraint
467-
func (con ArbOwner) ClearConstraint(c ctx, evm mech, resources []resourceWeight, periodSecs uint32) error {
467+
func (con ArbOwner) ClearConstraint(c ctx, evm mech, resources []uint8, periodSecs uint32) error {
468468
res, err := toArbOsResourceSet(resources)
469469
if err != nil {
470470
return err

0 commit comments

Comments
 (0)