Skip to content
Merged
Show file tree
Hide file tree
Changes from 10 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
12 changes: 11 additions & 1 deletion consensus/misc/eip1559/eip1559.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ func CalcBaseFee(config *params.ChainConfig, parent *types.Header) *big.Int {
return new(big.Int).SetUint64(params.InitialBaseFee)
}

parentGasTarget := parent.GasLimit / config.ElasticityMultiplier()
parentGasTarget := calcParentGasTarget(config, parent)
// If the parent gasUsed is the same as the target, the baseFee remains unchanged.
if parent.GasUsed == parentGasTarget {
return new(big.Int).Set(parent.BaseFee)
Expand Down Expand Up @@ -98,3 +98,13 @@ func CalcBaseFee(config *params.ChainConfig, parent *types.Header) *big.Int {
return baseFee
}
}

Comment thread
manav2401 marked this conversation as resolved.
// calcParentGasTarget calculates the target gas based on parent block gas limit. Earlier
// it was derived by `ElasticityMultiplier` as it had an integer multiplier value. Post
// dandeli HF, a percentage value will be used to calculate the gas target.
func calcParentGasTarget(config *params.ChainConfig, parent *types.Header) uint64 {
if config.Bor != nil && config.Bor.IsDandeli(parent.Number) {
return parent.GasLimit * params.TargetGasPercentagePostDandeli / 100
}
return parent.GasLimit / config.ElasticityMultiplier()
}
212 changes: 212 additions & 0 deletions consensus/misc/eip1559/eip1559_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,13 @@
package eip1559

import (
"fmt"
"math/big"
"testing"

"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/params"
"github.com/stretchr/testify/require"
)

// copyConfig does a _shallow_ copy of a given config. Safe to set new values, but
Expand Down Expand Up @@ -171,3 +173,213 @@
}
}
}

func TestCalcParentGasTarget(t *testing.T) {
t.Parallel()

testConfig := copyConfig(config())
testConfig.Bor.DandeliBlock = big.NewInt(20)

defaultGasLimit := uint64(60_000_000)

t.Run("gas target calculation pre dandeli HF", func(t *testing.T) {
block := &types.Header{
Number: big.NewInt(9),
GasLimit: defaultGasLimit,
GasUsed: defaultGasLimit / 2,
BaseFee: big.NewInt(params.InitialBaseFee),
}
gasTarget := calcParentGasTarget(testConfig, block)
expected := block.GasLimit / 2 // because elasticity multiplier is set to 2 by default
require.Equal(t, expected, gasTarget, "expected gas target = gaslimit/2")
})

t.Run("gas target calculation post dandeli HF", func(t *testing.T) {
block := &types.Header{
Number: big.NewInt(20),
GasLimit: defaultGasLimit,
GasUsed: defaultGasLimit / 2,
BaseFee: big.NewInt(params.InitialBaseFee),
}
gasTarget := calcParentGasTarget(testConfig, block)
expected := block.GasLimit * 6 / 10 // because gas target is 60% of total block gas limit
require.Equal(t, expected, gasTarget, "case #1: expected gas target = 60 percent of gas limit")

block = &types.Header{
Number: big.NewInt(21),
GasLimit: defaultGasLimit,
GasUsed: defaultGasLimit / 2,
BaseFee: big.NewInt(params.InitialBaseFee),
}
gasTarget = calcParentGasTarget(testConfig, block)
expected = block.GasLimit * 6 / 10 // because gas target is 60% of total block gas limit
require.Equal(t, expected, gasTarget, "case #2: expected gas target = 60 percent of gas limit")
})

t.Run("nil bor config", func(t *testing.T) {
testConfig.Bor = nil
block := &types.Header{
Number: big.NewInt(21),
GasLimit: defaultGasLimit,
GasUsed: defaultGasLimit / 2,
BaseFee: big.NewInt(params.InitialBaseFee),
}
gasTarget := calcParentGasTarget(testConfig, block)
expected := block.GasLimit / 2 // because elasticity multiplier is set to 2 by default
require.Equal(t, expected, gasTarget, "expected gas target = gaslimit/2 when bor config is nil")
})
}

// simpleBaseFeeCalculator contains an overly simplified logic of base fee calculations useful for generating
// expected values in test cases. It assumes all blocks are post-bhilai HF.
func simpleBaseFeeCalculator(initialBaseFee int64, gasLimit, gasUsed uint64, targetGasPercentage uint64) uint64 {
initial := big.NewInt(initialBaseFee)
val := big.NewInt(1)
val.Mul(val, initial)

// Assuming tests are running post bhilai
bfd := int64(params.BaseFeeChangeDenominatorPostBhilai)

// Define a target gas based on given percentage
target := gasLimit * targetGasPercentage / 100
if gasUsed == target {
return initial.Uint64()
}

// follow the simple formula to get the new base fee:
// base fee = initialBaseFee +/- (initialBaseFee * gasUsedDelta / gasTarget / baseFeeChangeDenominator)

var delta int64
if gasUsed > target {
delta = int64(gasUsed - target)
} else {
delta = int64(target - gasUsed)
}

val.Mul(val, big.NewInt(delta))
val.Div(val, big.NewInt(int64(bfd)))

Check failure on line 260 in consensus/misc/eip1559/eip1559_test.go

View workflow job for this annotation

GitHub Actions / lint (ubuntu-22.04)

unnecessary conversion (unconvert)
val.Div(val, big.NewInt(int64(target)))

if gasUsed > target {
return initial.Add(initial, val).Uint64()
} else {
return initial.Sub(initial, val).Uint64()
}
}

func TestCalcBaseFeeDandeli(t *testing.T) {
t.Parallel()

testConfig := copyConfig(config())
testConfig.Bor.BhilaiBlock = big.NewInt(8)
testConfig.Bor.DandeliBlock = big.NewInt(20)

// Case 1: Create pre-dandeli cases where HF is defined in future. Validate
// base fee calculations before HF kicks in. Base fee should be calculated
// based on default elasticity multiplier.
tests := []struct {
name string
parentBaseFee int64
parentGasLimit uint64
parentGasUsed uint64
expectedBaseFee uint64
}{
{"usage == target", params.InitialBaseFee, 60_000_000, 30_000_000, params.InitialBaseFee},
{"usage below target #1", params.InitialBaseFee, 60_000_000, 20_000_000, 994791667},
{"usage below target #2", params.InitialBaseFee, 60_000_000, 10_000_000, 989583334},
{"usage above target #1", params.InitialBaseFee, 60_000_000, 40_000_000, 1005208333},
{"usage above target #2", params.InitialBaseFee, 60_000_000, 50_000_000, 1010416666},
{"usage full", params.InitialBaseFee, 60_000_000, 60_000_000, 1015625000},
{"usage 0", params.InitialBaseFee, 60_000_000, 0, 984375000},
}
for _, test := range tests {
block := &types.Header{
Number: big.NewInt(8),
GasLimit: test.parentGasLimit,
GasUsed: test.parentGasUsed,
BaseFee: big.NewInt(test.parentBaseFee),
}
baseFee := CalcBaseFee(testConfig, block).Uint64()
expectedBaseFee := simpleBaseFeeCalculator(block.BaseFee.Int64(), block.GasLimit, block.GasUsed, 50)
require.Equal(
t,
expectedBaseFee,
baseFee,
fmt.Sprintf("pre-dandeli base fee mismatch with expected value, test: %s, got: %d, want: %d", test.name, baseFee, expectedBaseFee),
)
// Also check with manually calculated base fee
require.Equal(
t,
test.expectedBaseFee,
baseFee,
fmt.Sprintf("pre-dandeli base fee mismatch with manually calculated value, test: %s, got: %d, want: %d", test.name, baseFee, expectedBaseFee),
)
}

// Case 2: Create post-dandeli cases where HF has kicked in. Validate base fee changes
// based on the newly introduced protocol param: TargetGasPrecentage. Target gas limit
// should be calculated based on this percentage value out of total gas limit. Base
// fee should be changed accordingly.
tests = []struct {
name string
parentBaseFee int64
parentGasLimit uint64
parentGasUsed uint64
expectedBaseFee uint64
}{
{"usage == target (60%)", params.InitialBaseFee, 60_000_000, 36_000_000, params.InitialBaseFee},
{"usage below target #1", params.InitialBaseFee, 60_000_000, 30_000_000, 997395834},
{"usage below target #2", params.InitialBaseFee, 60_000_000, 10_000_000, 988715278},
{"usage above target #1", params.InitialBaseFee, 60_000_000, 40_000_000, 1001736111},
{"usage above target #2", params.InitialBaseFee, 60_000_000, 50_000_000, 1006076388},
{"usage full", params.InitialBaseFee, 60_000_000, 60_000_000, 1010416666},
{"usage 0", params.InitialBaseFee, 60_000_000, 0, 984375000},
}
for _, test := range tests {
// Post-dandeli block #1
block := &types.Header{
Number: big.NewInt(20),
GasLimit: test.parentGasLimit,
GasUsed: test.parentGasUsed,
BaseFee: big.NewInt(test.parentBaseFee),
}
baseFee := CalcBaseFee(testConfig, block).Uint64()
expectedBaseFee := simpleBaseFeeCalculator(block.BaseFee.Int64(), block.GasLimit, block.GasUsed, 60)
require.Equal(
t,
expectedBaseFee,
baseFee,
fmt.Sprintf("post-dandeli #1: base fee mismatch with expected value, test: %s, got: %d, want: %d", test.name, baseFee, expectedBaseFee),
)
// Also check with manually calculated base fee
require.Equal(
t,
test.expectedBaseFee,
baseFee,
fmt.Sprintf("post-dandeli #1: base fee mismatch with manually calculated value, test: %s, got: %d, want: %d", test.name, baseFee, expectedBaseFee),
)

// Post-dandeli block #2
block = &types.Header{
Number: big.NewInt(21),
GasLimit: test.parentGasLimit,
GasUsed: test.parentGasUsed,
BaseFee: big.NewInt(test.parentBaseFee),
}
baseFee = CalcBaseFee(testConfig, block).Uint64()
expectedBaseFee = simpleBaseFeeCalculator(block.BaseFee.Int64(), block.GasLimit, block.GasUsed, 60)
require.Equal(
t,
expectedBaseFee,
baseFee,
fmt.Sprintf("post-dandeli #2: base fee mismatch with expected value, test: %s, got: %d, want: %d", test.name, baseFee, expectedBaseFee),
)
// Also check with manually calculated base fee
require.Equal(
t,
test.expectedBaseFee,
baseFee,
fmt.Sprintf("post-dandeli #2: base fee mismatch with manually calculated value, test: %s, got: %d, want: %d", test.name, baseFee, expectedBaseFee),
)
}
}
5 changes: 5 additions & 0 deletions params/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -888,6 +888,7 @@ type BorConfig struct {
RioBlock *big.Int `json:"rioBlock"` // Rio switch block (nil = no fork, 0 = already on rio)
MadhugiriBlock *big.Int `json:"madhugiriBlock"` // Madhugiri switch block (nil = no fork, 0 = already on madhugiri)
MadhugiriProBlock *big.Int `json:"madhugiriProBlock"` // MadhugiriPro switch block (nil = no fork, 0 = already on madhugiriPro)
DandeliBlock *big.Int `json:"dandeliBlock"` // Dandeli switch block (nil = no fork, 0 = already on dandeli)
}

// String implements the stringer interface, returning the consensus engine details.
Expand Down Expand Up @@ -947,6 +948,10 @@ func (c *BorConfig) IsMadhugiriPro(number *big.Int) bool {
return isBlockForked(c.MadhugiriProBlock, number)
}

func (c *BorConfig) IsDandeli(number *big.Int) bool {
return isBlockForked(c.DandeliBlock, number)
}

// // TODO: modify this function once the block number is finalized
// func (c *BorConfig) IsNapoli(number *big.Int) bool {
// if c.NapoliBlock != nil {
Expand Down
3 changes: 3 additions & 0 deletions params/protocol_params.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,9 @@ const (
DefaultBaseFeeChangeDenominator = 8 // Bounds the amount the base fee can change between blocks.
DefaultElasticityMultiplier = 2 // Bounds the maximum gas limit an EIP-1559 block may have.

DefaultTargetGasPercentage = 50 // Specifies target block gas as percentage of block gas limit for EIP-1559
Comment thread
manav2401 marked this conversation as resolved.
TargetGasPercentagePostDandeli = 60 // Specifies target block gas as percentage of block gas limit for EIP-1559 after Dandeli hard fork
Comment thread
manav2401 marked this conversation as resolved.
Outdated

MaxCodeSize = 24576 // Maximum bytecode to permit for a contract
MaxCodeSizePostAhmedabad = 32768 // Maximum bytecode to permit for a contract post Ahmedabad hard fork (bor / polygon pos) (32KB)
MaxInitCodeSize = 2 * MaxCodeSize // Maximum initcode to permit in a creation transaction and create instructions
Expand Down
Loading