Skip to content

Commit

Permalink
Merge pull request #227 from testinprod-io/feature/mininny/holocene-1…
Browse files Browse the repository at this point in the history
…559-params

Holocene 1559 params for block building
  • Loading branch information
mininny authored Oct 31, 2024
2 parents 38558d5 + 302ea94 commit 8e48c72
Show file tree
Hide file tree
Showing 9 changed files with 327 additions and 159 deletions.
81 changes: 78 additions & 3 deletions consensus/misc/eip1559.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package misc

import (
"encoding/binary"
"fmt"
"math/big"

Expand Down Expand Up @@ -101,18 +102,92 @@ func (f eip1559Calculator) CurrentFees(chainConfig *chain.Config, db kv.Getter)
return baseFee, blobFee, minBlobGasPrice, currentHeader.GasLimit, nil
}

// CalcBaseFee calculates the basefee of the header.
// DecodeHolocene1559Params extracts the Holcene 1559 parameters from the encoded form:
// https://github.com/ethereum-optimism/specs/blob/main/specs/protocol/holocene/exec-engine.md#eip1559params-encoding
//
// Returns 0,0 if the format is invalid, though ValidateHolocene1559Params should be used instead of
// this function for validity checking.
func DecodeHolocene1559Params(params []byte) (uint64, uint64) {
if len(params) != 8 {
return 0, 0
}
denominator := binary.BigEndian.Uint32(params[:4])
elasticity := binary.BigEndian.Uint32(params[4:])
return uint64(denominator), uint64(elasticity)
}

// DecodeHoloceneExtraData decodes holocene extra data without performing full validation.
func DecodeHoloceneExtraData(extra []byte) (uint64, uint64) {
if len(extra) != 9 {
return 0, 0
}
return DecodeHolocene1559Params(extra[1:])
}

func EncodeHolocene1559Params(denom, elasticity uint32) []byte {
r := make([]byte, 8)
binary.BigEndian.PutUint32(r[:4], denom)
binary.BigEndian.PutUint32(r[4:], elasticity)
return r
}

func EncodeHoloceneExtraData(denom, elasticity uint32) []byte {
r := make([]byte, 9)
// leave version byte 0
binary.BigEndian.PutUint32(r[1:5], denom)
binary.BigEndian.PutUint32(r[5:], elasticity)
return r
}

// ValidateHolocene1559Params checks if the encoded parameters are valid according to the Holocene
// upgrade.
func ValidateHolocene1559Params(params []byte) error {
if len(params) != 8 {
return fmt.Errorf("holocene eip-1559 params should be 8 bytes, got %d", len(params))
}
d, e := DecodeHolocene1559Params(params)
if e != 0 && d == 0 {
return fmt.Errorf("holocene params cannot have a 0 denominator unless elasticity is also 0")
}
return nil
}

// ValidateHoloceneExtraData checks if the header extraData is valid according to the Holocene
// upgrade.
func ValidateHoloceneExtraData(extra []byte) error {
if len(extra) != 9 {
return fmt.Errorf("holocene extraData should be 9 bytes, got %d", len(extra))
}
if extra[0] != 0 {
return fmt.Errorf("holocene extraData should have 0 version byte, got %d", extra[0])
}
return ValidateHolocene1559Params(extra[1:])
}

// The time belongs to the new block to check which upgrades are active.
func CalcBaseFee(config *chain.Config, parent *types.Header, time uint64) *big.Int {
// If the current block is the first EIP-1559 block, return the InitialBaseFee.
if !config.IsLondon(parent.Number.Uint64()) {
return new(big.Int).SetUint64(params.InitialBaseFee)
}

elasticity := config.ElasticityMultiplier(params.ElasticityMultiplier)
denominator := getBaseFeeChangeDenominator(config, params.BaseFeeChangeDenominator, time)

if config.IsHolocene(parent.Time) {
denominator, elasticity = DecodeHoloceneExtraData(parent.Extra)
if denominator == 0 {
// this shouldn't happen as the ExtraData should have been validated prior
panic("invalid eip-1559 params in extradata")
}
}

var (
parentGasTarget = parent.GasLimit / config.ElasticityMultiplier(params.ElasticityMultiplier)
parentGasTarget = parent.GasLimit / elasticity
parentGasTargetBig = new(big.Int).SetUint64(parentGasTarget)
baseFeeChangeDenominator = new(big.Int).SetUint64(getBaseFeeChangeDenominator(config, parent.Number.Uint64(), time))
baseFeeChangeDenominator = new(big.Int).SetUint64(denominator)
)

// 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
41 changes: 41 additions & 0 deletions consensus/misc/eip1559_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ func opConfig() *chain.Config {
EIP1559Denominator: 50,
EIP1559DenominatorCanyon: 250,
}
ht := big.NewInt(12)
config.HoloceneTime = ht
return config
}

Expand Down Expand Up @@ -157,5 +159,44 @@ func TestCalcBaseFeeOptimism(t *testing.T) {
if have, want := CalcBaseFee(opConfig(), parent, parent.Time+2), big.NewInt(test.expectedBaseFee); have.Cmp(want) != 0 {
t.Errorf("test %d: have %d want %d, ", i, have, want)
}
if test.postCanyon {
// make sure Holocene activation doesn't change the outcome; since these tests have empty eip1559 params,
// they should be handled using the Canyon config.
parent.Time = 10
if have, want := CalcBaseFee(opConfig(), parent, parent.Time+2), big.NewInt(test.expectedBaseFee); have.Cmp(want) != 0 {
t.Errorf("test %d: have %d want %d, ", i, have, want)
}
}
}
}

// TestCalcBaseFeeHolocene assumes all blocks are Optimism blocks post-Holocene upgrade
func TestCalcBaseFeeOptimismHolocene(t *testing.T) {
parentBaseFee := int64(10_000_000)
parentGasLimit := uint64(30_000_000)
tests := []struct {
parentGasUsed uint64
expectedBaseFee int64
denom, elasticity uint32
}{
{parentGasLimit / 2, parentBaseFee, 10, 2}, // target
{10_000_000, 9_666_667, 10, 2}, // below
{20_000_000, 10_333_333, 10, 2}, // above
{parentGasLimit / 10, parentBaseFee, 2, 10}, // target
{1_000_000, 6_666_667, 2, 10}, // below
{30_000_000, 55_000_000, 2, 10}, // above
}
for i, test := range tests {
parent := &types.Header{
Number: common.Big32,
GasLimit: parentGasLimit,
GasUsed: test.parentGasUsed,
BaseFee: big.NewInt(parentBaseFee),
Time: 12,
Extra: EncodeHoloceneExtraData(test.denom, test.elasticity),
}
if have, want := CalcBaseFee(opConfig(), parent, parent.Time+2), big.NewInt(test.expectedBaseFee); have.Cmp(want) != 0 {
t.Errorf("test %d: have %d want %d, ", i, have, want)
}
}
}
1 change: 1 addition & 0 deletions core/block_builder_parameters.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,5 @@ type BlockBuilderParameters struct {
Transactions [][]byte
NoTxPool bool
GasLimit *uint64
EIP1559Params []byte
}
Loading

0 comments on commit 8e48c72

Please sign in to comment.