Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions arbitrum/multigas/constraint.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package multigas

import "time"

// The period duration for a resource constraint.
type PeriodSecs uint32

// resourceConstraint defines the max gas target per second for the given period for a single resource.
type resourceConstraint struct {
period time.Duration
target uint64
}

// ResourceConstraints is a set of constraints for all resources.
//
// The chain owner defines constraints to limit the usage of each resource. A resource can have
// multiple constraints with different periods, but there may be a single constraint given the
// resource and period.
//
// Example constraints:
// - X amount of computation over 12 seconds so nodes can keep up.
// - Y amount of computation over 7 days so fresh nodes can catch up with the chain.
// - Z amount of history growth over one month to avoid bloat.
type ResourceConstraints map[ResourceKind]map[PeriodSecs]resourceConstraint

// NewResourceConstraints creates a new set of constraints.
// This type can be used as a reference.
func NewResourceConstraints() ResourceConstraints {
c := ResourceConstraints{}
for resource := ResourceKindUnknown + 1; resource < NumResourceKind; resource++ {
c[resource] = map[PeriodSecs]resourceConstraint{}
}
return c
}

// SetConstraint adds or updates the given resource constraint.
func (rc ResourceConstraints) SetConstraint(
resource ResourceKind, periodSecs PeriodSecs, targetPerPeriod uint64,
) {
rc[resource][periodSecs] = resourceConstraint{
period: time.Duration(periodSecs) * time.Second,
target: targetPerPeriod / uint64(periodSecs),
}
}

// ClearConstraint removes the given resource constraint.
func (rc ResourceConstraints) ClearConstraint(resource ResourceKind, periodSecs PeriodSecs) {
delete(rc[resource], periodSecs)
}
53 changes: 53 additions & 0 deletions arbitrum/multigas/constraint_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package multigas

import (
"testing"
"time"
)

func assertEqual[T comparable](t *testing.T, description string, got T, want T) {
t.Helper()
if got != want {
t.Fatalf("unexpected %v: want %v, got %v", description, want, got)
}
}

func TestResourceConstraints(t *testing.T) {
rc := NewResourceConstraints()

const (
minuteSecs = 60
daySecs = 24 * 60 * 60
weekSecs = 7 * daySecs
monthSecs = 30 * daySecs
)

// Adds a few constraints
rc.SetConstraint(ResourceKindComputation, minuteSecs, 5_000_000*minuteSecs)
rc.SetConstraint(ResourceKindComputation, weekSecs, 3_000_000*weekSecs)
rc.SetConstraint(ResourceKindHistoryGrowth, monthSecs, 1_000_000*monthSecs)
assertEqual(t, "number of computation constraints", len(rc[ResourceKindComputation]), 2)
assertEqual(t, "constraint period", rc[ResourceKindComputation][minuteSecs].period, time.Duration(minuteSecs)*time.Second)
assertEqual(t, "constraint target", rc[ResourceKindComputation][minuteSecs].target, 5_000_000)
assertEqual(t, "constraint period", rc[ResourceKindComputation][weekSecs].period, time.Duration(weekSecs)*time.Second)
assertEqual(t, "constraint target", rc[ResourceKindComputation][weekSecs].target, 3_000_000)
assertEqual(t, "number of history growth constraints", len(rc[ResourceKindHistoryGrowth]), 1)
assertEqual(t, "constraint period", rc[ResourceKindHistoryGrowth][monthSecs].period, time.Duration(monthSecs)*time.Second)
assertEqual(t, "constraint target", rc[ResourceKindHistoryGrowth][monthSecs].target, 1_000_000)
assertEqual(t, "number of storage access constraints", len(rc[ResourceKindStorageAccess]), 0)
assertEqual(t, "number of storage growth constraints", len(rc[ResourceKindStorageGrowth]), 0)

// Updates a constraint
rc.SetConstraint(ResourceKindHistoryGrowth, monthSecs, 500_000*monthSecs)
assertEqual(t, "number of history growth constraints", len(rc[ResourceKindHistoryGrowth]), 1)
assertEqual(t, "constraint target", rc[ResourceKindHistoryGrowth][monthSecs].target, 500_000)

// Clear constraints
rc.ClearConstraint(ResourceKindComputation, minuteSecs)
rc.ClearConstraint(ResourceKindComputation, weekSecs)
rc.ClearConstraint(ResourceKindHistoryGrowth, monthSecs)
assertEqual(t, "number of computation constraints", len(rc[ResourceKindComputation]), 0)
assertEqual(t, "number of history growth constraints", len(rc[ResourceKindHistoryGrowth]), 0)
assertEqual(t, "number of storage access constraints", len(rc[ResourceKindStorageAccess]), 0)
assertEqual(t, "number of storage growth constraints", len(rc[ResourceKindStorageGrowth]), 0)
}
7 changes: 7 additions & 0 deletions arbitrum/multigas/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// Package multigas defines multi-dimensional gas for the EVM.
//
// This package introduces mechanisms to track each resource used by the EVM separately. The
// possible resources are computation, history growth, storage access, and storage growth. By
// tracking each one individually and setting specific constraints, we can increase the overall gas
// target for the chain.
package multigas
16 changes: 16 additions & 0 deletions arbitrum/multigas/resources.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package multigas

// ResourceKind represents a dimension for the multi-dimensional gas.
type ResourceKind uint8

const (
ResourceKindUnknown ResourceKind = iota
ResourceKindComputation
ResourceKindHistoryGrowth
ResourceKindStorageAccess
ResourceKindStorageGrowth
NumResourceKind
)

// MultiGas tracks gas for each resource separately.
type MultiGas [NumResourceKind]uint64
Loading