Skip to content

Commit

Permalink
adding helpers for updating util
Browse files Browse the repository at this point in the history
  • Loading branch information
davidterpay committed Nov 14, 2023
1 parent 5a5c61c commit fb0cee2
Show file tree
Hide file tree
Showing 6 changed files with 300 additions and 145 deletions.
8 changes: 8 additions & 0 deletions proto/feemarket/feemarket/v1/genesis.proto
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,12 @@ message State {

// Index is the index of the current block in the block utilization window.
uint64 index = 4;

// MaxBlockUtilization is the maximum utilization of a given block. This is denominated
// in the number of gas units consumed per block.
uint64 max_block_utilization = 5;

// TargetBlockUtilization is the target utilization of a given block. This is denominated
// in the number of gas units consumed per block.
uint64 target_block_utilization = 6;
}
8 changes: 7 additions & 1 deletion x/feemarket/types/eip1559.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,13 @@ func DefaultParams() Params {
// DefaultState returns the default state for the EIP-1559 fee market
// implementation without the AIMD learning rate adjustment algorithm.
func DefaultState() State {
return NewState(DefaultWindow, DefaultMinBaseFee, DefaultMinLearningRate)
return NewState(
DefaultWindow,
DefaultTargetBlockSize,
DefaultMaxBlockSize,
DefaultMinBaseFee,
DefaultMinLearningRate,
)
}

// DefaultGenesisState returns a default genesis state that implements
Expand Down
8 changes: 7 additions & 1 deletion x/feemarket/types/eip1559_aimd.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,13 @@ func DefaultAIMDParams() Params {
// block utilization and dynamically adjusts the learning rate based on the
// utilization within the window.
func DefaultAIMDState() State {
return NewState(DefaultAIMDWindow, DefaultAIMDMinBaseFee, DefaultAIMDMinLearningRate)
return NewState(
DefaultAIMDWindow,
DefaultAIMDTargetBlockSize,
DefaultAIMDMaxBlockSize,
DefaultAIMDMinBaseFee,
DefaultAIMDMinLearningRate,
)
}

// DefaultAIMDGenesisState returns a default genesis state that implements
Expand Down
138 changes: 107 additions & 31 deletions x/feemarket/types/genesis.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

89 changes: 62 additions & 27 deletions x/feemarket/types/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,41 +9,64 @@ import (
// NewState instantiates a new fee market state instance. This is utilized
// to implement both the base EIP-1559 fee market implementation and the
// AIMD EIP-1559 fee market implementation.
func NewState(window uint64, baseFee math.Int, learningRate math.LegacyDec) State {
func NewState(
window,
target, max uint64,
baseFee math.Int,
learningRate math.LegacyDec,
) State {
return State{
Window: make([]uint64, window),
BaseFee: baseFee,
LearningRate: learningRate,
Index: 0,
Window: make([]uint64, window),
BaseFee: baseFee,
LearningRate: learningRate,
Index: 0,
TargetBlockUtilization: target,
MaxBlockUtilization: max,
}
}

// Update updates the block utilization for the current height with the given
// transaction utilization i.e. gas limit.
func (s *State) Update(gas uint64) error {
update := s.Window[s.Index] + gas
if update > s.MaxBlockUtilization {
return fmt.Errorf("block utilization cannot exceed max block utilization")
}

s.Window[s.Index] = update
return nil
}

// IncrementHeight increments the current height of the state. This is
// utilized to update the block utilization window.
func (s *State) IncrementHeight() {
s.Index = (s.Index + 1) % uint64(len(s.Window))
s.Window[s.Index] = 0
}

// UpdateBaseFee updates the learning rate and base fee based on the AIMD
// learning rate adjustment algorithm. The learning rate is updated
// based on the average utilization of the block window. The base fee is
// update using the new learning rate and the delta adjustment. Please
// see the EIP-1559 specification for more details.
func (s *State) UpdateBaseFee(params Params) math.Int {
// Update the learning rate.
newLR := s.UpdateLearningRate(params)

func (s *State) UpdateBaseFee(delta math.LegacyDec) math.Int {
// Calculate the new base fee with the learning rate adjustment.
currentBlockSize := math.LegacyNewDecFromInt(math.NewIntFromUint64(s.Window[s.Index]))
targetBlockSize := math.LegacyNewDecFromInt(math.NewIntFromUint64(params.TargetBlockUtilization))
targetBlockSize := math.LegacyNewDecFromInt(math.NewIntFromUint64(s.TargetBlockUtilization))
utilization := (currentBlockSize.Sub(targetBlockSize)).Quo(targetBlockSize)

// Truncate the learning rate adjustment to an integer.
//
// This is equivalent to
// 1 + (learningRate * (currentBlockSize - targetBlockSize) / targetBlockSize)
learningRateAdjustment := math.LegacyOneDec().Add(newLR.Mul(utilization))
learningRateAdjustment := math.LegacyOneDec().Add(s.LearningRate.Mul(utilization))

// Calculate the delta adjustment.
net := s.GetNetUtilization(params.TargetBlockUtilization)
delta := params.Delta.Mul(math.LegacyNewDecFromInt(net))
net := math.LegacyNewDecFromInt(s.GetNetUtilization()).Mul(delta)

// Update the base fee.
s.BaseFee = (math.LegacyNewDecFromInt(s.BaseFee).Mul(learningRateAdjustment)).Add(delta).TruncateInt()
fee := (math.LegacyNewDecFromInt(s.BaseFee).Mul(learningRateAdjustment)).Add(net).TruncateInt()
s.BaseFee = fee
return s.BaseFee
}

Expand All @@ -60,22 +83,22 @@ func (s *State) UpdateBaseFee(params Params) math.Int {
// when blocks are relatively close to the target block utilization.
//
// For more details, please see the EIP-1559 specification.
func (s *State) UpdateLearningRate(params Params) math.LegacyDec {
func (s *State) UpdateLearningRate(theta, alpha, beta, minLR, maxLR math.LegacyDec) math.LegacyDec {
// Calculate the average utilization of the block window.
avg := s.GetAverageUtilization(params.MaxBlockUtilization)
avg := s.GetAverageUtilization()

// Determine if the average utilization is above or below the target
// threshold and adjust the learning rate accordingly.
var updatedLearningRate math.LegacyDec
if avg.LTE(params.Theta) || avg.GTE(math.LegacyOneDec().Sub(params.Theta)) {
updatedLearningRate = params.Alpha.Add(s.LearningRate)
if updatedLearningRate.GT(params.MaxLearningRate) {
updatedLearningRate = params.MaxLearningRate
if avg.LTE(theta) || avg.GTE(math.LegacyOneDec().Sub(theta)) {
updatedLearningRate = alpha.Add(s.LearningRate)
if updatedLearningRate.GT(maxLR) {
updatedLearningRate = maxLR
}
} else {
updatedLearningRate = s.LearningRate.Mul(params.Beta)
if updatedLearningRate.LT(params.MinLearningRate) {
updatedLearningRate = params.MinLearningRate
updatedLearningRate = s.LearningRate.Mul(beta)
if updatedLearningRate.LT(minLR) {
updatedLearningRate = minLR
}
}

Expand All @@ -85,10 +108,10 @@ func (s *State) UpdateLearningRate(params Params) math.LegacyDec {
}

// GetNetUtilization returns the net utilization of the block window.
func (s *State) GetNetUtilization(target uint64) math.Int {
func (s *State) GetNetUtilization() math.Int {
net := math.NewInt(0)

targetUtilization := math.NewIntFromUint64(target)
targetUtilization := math.NewIntFromUint64(s.TargetBlockUtilization)
for _, utilization := range s.Window {
diff := math.NewIntFromUint64(utilization).Sub(targetUtilization)
net = net.Add(diff)
Expand All @@ -99,7 +122,7 @@ func (s *State) GetNetUtilization(target uint64) math.Int {

// GetAverageUtilization returns the average utilization of the block
// window.
func (s *State) GetAverageUtilization(max uint64) math.LegacyDec {
func (s *State) GetAverageUtilization() math.LegacyDec {
var total uint64
for _, utilization := range s.Window {
total += utilization
Expand All @@ -108,13 +131,25 @@ func (s *State) GetAverageUtilization(max uint64) math.LegacyDec {
sum := math.LegacyNewDecFromInt(math.NewIntFromUint64(total))

multiple := math.LegacyNewDecFromInt(math.NewIntFromUint64(uint64(len(s.Window))))
divisor := math.LegacyNewDecFromInt(math.NewIntFromUint64(max)).Mul(multiple)
divisor := math.LegacyNewDecFromInt(math.NewIntFromUint64(s.MaxBlockUtilization)).Mul(multiple)

return sum.Quo(divisor)
}

// ValidateBasic performs basic validation on the state.
func (s *State) ValidateBasic() error {
if s.MaxBlockUtilization == 0 {
return fmt.Errorf("max utilization cannot be zero")
}

if s.TargetBlockUtilization == 0 {
return fmt.Errorf("target utilization cannot be zero")
}

if s.TargetBlockUtilization > s.MaxBlockUtilization {
return fmt.Errorf("target utilization cannot be greater than max utilization")
}

if s.Window == nil || len(s.Window) == 0 {
return fmt.Errorf("block utilization window cannot be nil or empty")
}
Expand Down
Loading

0 comments on commit fb0cee2

Please sign in to comment.