Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
5 changes: 1 addition & 4 deletions arbos/internal_tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,9 +94,6 @@ func ApplyInternalTxUpdate(tx *types.ArbitrumInternalTx, state *arbosState.Arbos
oldL1BlockNumber, err := state.Blockhashes().L1BlockNumber()
state.Restrict(err)

l2BaseFee, err := state.L2PricingState().BaseFeeWei()
state.Restrict(err)

if l1BlockNumber > oldL1BlockNumber {
state.Restrict(state.Blockhashes().RecordNewL1Block(l1BlockNumber-1, prevHash, state.ArbOSVersion()))
}
Expand All @@ -107,7 +104,7 @@ func ApplyInternalTxUpdate(tx *types.ArbitrumInternalTx, state *arbosState.Arbos
_ = state.RetryableState().TryToReapOneRetryable(currentTime, evm, util.TracingDuringEVM)
_ = state.RetryableState().TryToReapOneRetryable(currentTime, evm, util.TracingDuringEVM)

state.L2PricingState().UpdatePricingModel(l2BaseFee, timePassed, false)
state.L2PricingState().UpdatePricingModel(timePassed, state.ArbOSVersion())

return state.UpgradeArbosVersionIfNecessary(currentTime, evm.StateDB, evm.ChainConfig())
case InternalTxBatchPostingReportMethodID:
Expand Down
50 changes: 33 additions & 17 deletions arbos/l2pricing/l2pricing.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,32 +13,32 @@ import (

const (
gasConstraintTargetOffset uint64 = iota
gasConstraintInertiaOffset
gasConstraintAdjustmentWindowOffset
gasConstraintBacklogOffset
)

// GasConstraint tries to keep the gas backlog under the target (per second) for the given inertia factor.
// The inertia can be computed as 30*sqrt(period), where period is the number of seconds which the
// constraint is acting over. For instance, the default inertia of 102 is an approximation of 30*sqrt(12).
// GasConstraint tries to keep the gas backlog under the target (per second) for the given adjustment window.
// Target stands for gas usage per second
// Adjustment window is the time frame over which the price will rise by a factor of e if demand is 2x the target
type GasConstraint struct {
target storage.StorageBackedUint64
inertia storage.StorageBackedUint64
backlog storage.StorageBackedUint64
target storage.StorageBackedUint64
adjustmentWindow storage.StorageBackedUint64
backlog storage.StorageBackedUint64
}

func OpenGasConstraint(storage *storage.Storage) *GasConstraint {
return &GasConstraint{
target: storage.OpenStorageBackedUint64(gasConstraintTargetOffset),
inertia: storage.OpenStorageBackedUint64(gasConstraintInertiaOffset),
backlog: storage.OpenStorageBackedUint64(gasConstraintBacklogOffset),
target: storage.OpenStorageBackedUint64(gasConstraintTargetOffset),
adjustmentWindow: storage.OpenStorageBackedUint64(gasConstraintAdjustmentWindowOffset),
backlog: storage.OpenStorageBackedUint64(gasConstraintBacklogOffset),
}
}

func (c *GasConstraint) Clear() error {
if err := c.target.Clear(); err != nil {
return err
}
if err := c.inertia.Clear(); err != nil {
if err := c.adjustmentWindow.Clear(); err != nil {
return err
}
if err := c.backlog.Clear(); err != nil {
Expand All @@ -47,6 +47,22 @@ func (c *GasConstraint) Clear() error {
return nil
}

func (c *GasConstraint) Target() (uint64, error) {
return c.target.Get()
}

func (c *GasConstraint) AdjustmentWindow() (uint64, error) {
return c.adjustmentWindow.Get()
}

func (c *GasConstraint) Backlog() (uint64, error) {
return c.backlog.Get()
}

func (c *GasConstraint) SetBacklog(val uint64) error {
return c.backlog.Set(val)
}

type L2PricingState struct {
storage *storage.Storage
speedLimitPerSecond storage.StorageBackedUint64
Expand Down Expand Up @@ -171,15 +187,15 @@ func (ps *L2PricingState) Restrict(err error) {
ps.storage.Burner().Restrict(err)
}

func (ps *L2PricingState) SetConstraintsFromLegacy() error {
func (ps *L2PricingState) setConstraintsFromLegacy() error {
if err := ps.ClearConstraints(); err != nil {
return err
}
target, err := ps.SpeedLimitPerSecond()
if err != nil {
return err
}
inertia, err := ps.PricingInertia()
adjustmentWindow, err := ps.PricingInertia()
if err != nil {
return err
}
Expand All @@ -192,10 +208,10 @@ func (ps *L2PricingState) SetConstraintsFromLegacy() error {
return err
}
backlog := arbmath.SaturatingUSub(oldBacklog, arbmath.SaturatingUMul(backlogTolerance, target))
return ps.AddConstraint(target, inertia, backlog)
return ps.AddConstraint(target, adjustmentWindow, backlog)
}

func (ps *L2PricingState) AddConstraint(target uint64, inertia uint64, backlog uint64) error {
func (ps *L2PricingState) AddConstraint(target uint64, adjustmentWindow uint64, backlog uint64) error {
subStorage, err := ps.constraints.Push()
if err != nil {
return fmt.Errorf("failed to push constraint: %w", err)
Expand All @@ -204,8 +220,8 @@ func (ps *L2PricingState) AddConstraint(target uint64, inertia uint64, backlog u
if err := constraint.target.Set(target); err != nil {
return fmt.Errorf("failed to set target: %w", err)
}
if err := constraint.inertia.Set(inertia); err != nil {
return fmt.Errorf("failed to set inertia: %w", err)
if err := constraint.adjustmentWindow.Set(adjustmentWindow); err != nil {
return fmt.Errorf("failed to set adjustment window: %w", err)
}
if err := constraint.backlog.Set(backlog); err != nil {
return fmt.Errorf("failed to set backlog: %w", err)
Expand Down
9 changes: 5 additions & 4 deletions arbos/l2pricing/l2pricing_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,10 @@ func PricingForTest(t *testing.T) *L2PricingState {
}

func fakeBlockUpdate(t *testing.T, pricing *L2PricingState, gasUsed int64, timePassed uint64) {
basefee := getPrice(t, pricing)
pricing.storage.Burner().Restrict(pricing.AddToGasPool(-gasUsed))
pricing.UpdatePricingModel(arbmath.UintToBig(basefee), timePassed, true)
t.Helper()

pricing.storage.Burner().Restrict(pricing.addToGasPoolLegacy(-gasUsed))
pricing.updatePricingModelLegacy(timePassed)
}

func TestPricingModelExp(t *testing.T) {
Expand Down Expand Up @@ -133,7 +134,7 @@ func TestGasConstraints(t *testing.T) {
if want := 100*i + 1; target != want {
t.Errorf("wrong target: got %v, want %v", target, want)
}
inertia, err := constraint.inertia.Get()
inertia, err := constraint.adjustmentWindow.Get()
Require(t, err)
if want := 100*i + 2; inertia != want {
t.Errorf("wrong inertia: got %v, want %v", inertia, want)
Expand Down
51 changes: 42 additions & 9 deletions arbos/l2pricing/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import (
"github.com/offchainlabs/nitro/util/arbmath"
)

const ArbosMultiConstraintsVersion = params.ArbosVersion_50

const InitialSpeedLimitPerSecondV0 = 1000000
const InitialPerBlockGasLimitV0 uint64 = 20 * 1000000
const InitialSpeedLimitPerSecondV6 = 7000000
Expand All @@ -22,7 +24,29 @@ const InitialPricingInertia = 102
const InitialBacklogTolerance = 10
const InitialPerTxGasLimitV50 uint64 = 32 * 1000000

func (ps *L2PricingState) AddToGasPool(gas int64) error {
func (ps *L2PricingState) ShouldUseMultiConstraints(arbosVersion uint64) (bool, error) {
if arbosVersion >= ArbosMultiConstraintsVersion {
constraintsLength, err := ps.ConstraintsLength()
if err != nil {
return false, err
}
return constraintsLength > 0, nil
}
return false, nil
}

func (ps *L2PricingState) AddToGasPool(gas int64, arbosVersion uint64) error {
shouldUseMultiConstraints, err := ps.ShouldUseMultiConstraints(arbosVersion)
if err != nil {
return err
}
if shouldUseMultiConstraints {
return ps.addToGasPoolMultiConstraints(gas)
}
return ps.addToGasPoolLegacy(gas)
}

func (ps *L2PricingState) addToGasPoolLegacy(gas int64) error {
backlog, err := ps.GasBacklog()
if err != nil {
return err
Expand All @@ -31,7 +55,7 @@ func (ps *L2PricingState) AddToGasPool(gas int64) error {
return ps.SetGasBacklog(backlog)
}

func (ps *L2PricingState) AddToGasPoolMultiConstraints(gas int64) error {
func (ps *L2PricingState) addToGasPoolMultiConstraints(gas int64) error {
constraintsLength, err := ps.constraints.Length()
if err != nil {
return fmt.Errorf("failed to get number of constraints: %w", err)
Expand All @@ -50,6 +74,16 @@ func (ps *L2PricingState) AddToGasPoolMultiConstraints(gas int64) error {
return nil
}

// UpdatePricingModel updates the pricing model with info from the last block
func (ps *L2PricingState) UpdatePricingModel(timePassed uint64, arbosVersion uint64) {
shouldUseMultiConstraints, _ := ps.ShouldUseMultiConstraints(arbosVersion)
if shouldUseMultiConstraints {
ps.updatePricingModelMultiConstraints(timePassed)
} else {
ps.updatePricingModelLegacy(timePassed)
}
}

// applyGasDelta grows the backlog if the gas is negative and pays off if the gas is positive.
func applyGasDelta(backlog uint64, gas int64) uint64 {
if gas > 0 {
Expand All @@ -59,10 +93,9 @@ func applyGasDelta(backlog uint64, gas int64) uint64 {
}
}

// UpdatePricingModel updates the pricing model with info from the last block
func (ps *L2PricingState) UpdatePricingModel(l2BaseFee *big.Int, timePassed uint64, debug bool) {
func (ps *L2PricingState) updatePricingModelLegacy(timePassed uint64) {
speedLimit, _ := ps.SpeedLimitPerSecond()
_ = ps.AddToGasPool(arbmath.SaturatingCast[int64](arbmath.SaturatingUMul(timePassed, speedLimit)))
_ = ps.addToGasPoolLegacy(arbmath.SaturatingCast[int64](arbmath.SaturatingUMul(timePassed, speedLimit)))
inertia, _ := ps.PricingInertia()
tolerance, _ := ps.BacklogTolerance()
backlog, _ := ps.GasBacklog()
Expand All @@ -76,23 +109,23 @@ func (ps *L2PricingState) UpdatePricingModel(l2BaseFee *big.Int, timePassed uint
_ = ps.SetBaseFeeWei(baseFee)
}

func (ps *L2PricingState) UpdatePricingModelMultiConstraints(timePassed uint64) {
func (ps *L2PricingState) updatePricingModelMultiConstraints(timePassed uint64) {
// Compute exponent used in the basefee formula
totalExponent := arbmath.Bips(0)
constraintsLength, _ := ps.constraints.Length()
for i := range constraintsLength {
constraint := ps.OpenConstraintAt(i)
target, _ := constraint.target.Get()
target, _ := constraint.Target()

// Pay off backlog
backlog, _ := constraint.backlog.Get()
backlog, _ := constraint.Backlog()
gas := arbmath.SaturatingCast[int64](arbmath.SaturatingUMul(timePassed, target))
backlog = applyGasDelta(backlog, gas)
_ = constraint.backlog.Set(backlog)

// Calculate exponent with the formula backlog/divisor
if backlog > 0 {
inertia, _ := constraint.inertia.Get()
inertia, _ := constraint.AdjustmentWindow()
divisor := arbmath.SaturatingCastToBips(arbmath.SaturatingUMul(inertia, target))
exponent := arbmath.NaturalToBips(arbmath.SaturatingCast[int64](backlog)) / divisor
totalExponent = arbmath.SaturatingBipsAdd(totalExponent, exponent)
Expand Down
6 changes: 3 additions & 3 deletions arbos/l2pricing/model_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,12 @@ func TestCompareLegacyPricingModelWithMultiConstraints(t *testing.T) {
_ = pricing.gasBacklog.Set(backlog)

// Initialize with a single constraint based on the legacy model
_ = pricing.SetConstraintsFromLegacy()
_ = pricing.setConstraintsFromLegacy()

pricing.UpdatePricingModel(nil, timePassed, false)
pricing.updatePricingModelLegacy(timePassed)
legacyPrice, _ := pricing.baseFeeWei.Get()

pricing.UpdatePricingModelMultiConstraints(timePassed)
pricing.updatePricingModelMultiConstraints(timePassed)
multiPrice, _ := pricing.baseFeeWei.Get()

if timePassed == 0 {
Expand Down
4 changes: 2 additions & 2 deletions arbos/tx_processor.go
Original file line number Diff line number Diff line change
Expand Up @@ -632,7 +632,7 @@ func (p *TxProcessor) EndTxHook(gasLeft uint64, success bool) {
}
}
// we've already credited the network fee account, but we didn't charge the gas pool yet
p.state.Restrict(p.state.L2PricingState().AddToGasPool(-arbmath.SaturatingCast[int64](gasUsed)))
p.state.Restrict(p.state.L2PricingState().AddToGasPool(-arbmath.SaturatingCast[int64](gasUsed), p.state.ArbOSVersion()))
return
}

Expand Down Expand Up @@ -695,7 +695,7 @@ func (p *TxProcessor) EndTxHook(gasLeft uint64, success bool) {
log.Error("total gas used < poster gas component", "gasUsed", gasUsed, "posterGas", p.posterGas)
computeGas = gasUsed
}
p.state.Restrict(p.state.L2PricingState().AddToGasPool(-arbmath.SaturatingCast[int64](computeGas)))
p.state.Restrict(p.state.L2PricingState().AddToGasPool(-arbmath.SaturatingCast[int64](computeGas), p.state.ArbOSVersion()))
}
}

Expand Down
2 changes: 1 addition & 1 deletion nitro-testnode
34 changes: 29 additions & 5 deletions precompiles/ArbGasInfo.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
package precompiles

import (
"fmt"
"math/big"

"github.com/ethereum/go-ethereum/common"
Expand Down Expand Up @@ -288,12 +287,37 @@ func (con ArbGasInfo) GetLastL1PricingSurplus(c ctx, evm mech) (*big.Int, error)

// GetMaxBlockGasLimit gets the maximum block gas limit
func (con ArbGasInfo) GetMaxBlockGasLimit(c ctx, evm mech) (uint64, error) {
// todo: update with real code once https://github.com/OffchainLabs/nitro/pull/3860 is merged
return 0, fmt.Errorf("GetMaxBlockGasLimit is a stub implementation, since definitions were upstreamed, but implementations not done")
return c.State.L2PricingState().PerBlockGasLimit()
}

// GetGasPricingConstraints gets the current gas pricing constraints used by the Multi-Constraint Pricer.
func (con ArbGasInfo) GetGasPricingConstraints(c ctx, evm mech) ([][3]uint64, error) {
// todo: update with real code once https://github.com/OffchainLabs/nitro/pull/3860 is merged
return nil, fmt.Errorf("GetGasPricingConstraints is a stub implementation, since definitions were upstreamed, but implementations not done")
len, err := c.State.L2PricingState().ConstraintsLength()
if err != nil {
return nil, err
}

constraints := make([][3]uint64, 0, len)
for i := range len {
constraint := c.State.L2PricingState().OpenConstraintAt(i)
gasTargetPerSecond, err := constraint.Target()
if err != nil {
return nil, err
}
adjustmentWindowSeconds, err := constraint.AdjustmentWindow()
if err != nil {
return nil, err
}
backlog, err := constraint.Backlog()
Comment thread
MishkaRogachev marked this conversation as resolved.
if err != nil {
return nil, err
}

constraints = append(constraints, [3]uint64{
gasTargetPerSecond,
adjustmentWindowSeconds,
backlog,
})
}
return constraints, nil
}
29 changes: 26 additions & 3 deletions precompiles/ArbOwner.go
Original file line number Diff line number Diff line change
Expand Up @@ -454,8 +454,31 @@ func (con ArbOwner) SetCalldataPriceIncrease(c ctx, _ mech, enable bool) error {
return c.State.Features().SetCalldataPriceIncrease(enable)
}

// SetGasPricingConstraints sets the gas pricing constraints used by the Multi-Constraint Pricer.
// SetGasBacklog sets the L2 gas backlog directly (used by single-constraint pricing model only)
func (con ArbOwner) SetGasBacklog(c ctx, evm mech, backlog uint64) error {
return c.State.L2PricingState().SetGasBacklog(backlog)
}

// SetGasPricingConstraints sets the gas pricing constraints used by the multi-constraint pricing model
func (con ArbOwner) SetGasPricingConstraints(c ctx, evm mech, constraints [][3]uint64) error {
// todo: update with real code once https://github.com/OffchainLabs/nitro/pull/3860 is merged
return fmt.Errorf("SetGasPricingConstraints is a stub implementation, since definitions were upstreamed, but implementations not done")
err := c.State.L2PricingState().ClearConstraints()
if err != nil {
return fmt.Errorf("failed to clear existing constraints: %w", err)
}

for _, constraint := range constraints {
gasTargetPerSecond := constraint[0]
adjustmentWindowSeconds := constraint[1]
startingBacklogValue := constraint[2]

if gasTargetPerSecond == 0 || adjustmentWindowSeconds == 0 {
return fmt.Errorf("invalid constraint with target %d and adjustment window %d", gasTargetPerSecond, adjustmentWindowSeconds)
}

err := c.State.L2PricingState().AddConstraint(gasTargetPerSecond, adjustmentWindowSeconds, startingBacklogValue)
if err != nil {
return fmt.Errorf("failed to add constraint (target: %d, adjustment window: %d): %w", gasTargetPerSecond, adjustmentWindowSeconds, err)
}
}
return nil
}
Loading
Loading