Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Holocene 1559 params for block building #227

Open
wants to merge 2 commits into
base: feature/mininny/holocene-hardfork
Choose a base branch
from
Open
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
44 changes: 41 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,55 @@ 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
func DecodeHolocene1559Params(params types.BlockNonce) (uint64, uint64) {
elasticity := binary.BigEndian.Uint32(params[4:])
denominator := binary.BigEndian.Uint32(params[:4])
return uint64(elasticity), uint64(denominator)
}

func EncodeHolocene1559Params(elasticity, denom uint32) types.BlockNonce {
var nonce types.BlockNonce
binary.BigEndian.PutUint32(nonce[4:], elasticity)
binary.BigEndian.PutUint32(nonce[:4], denom)
return nonce
}

// ValidateHoloceneParams checks if the encoded parameters are valid according to the Holocene
// upgrade.
func ValidateHoloceneParams(params types.BlockNonce) error {
e, d := DecodeHolocene1559Params(params)
if e != 0 && d == 0 {
return fmt.Errorf("holocene params cannot have a 0 denominator unless elasticity is also 0")
}
return nil
}

// 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(time) {
// Holocene requires we get the 1559 parameters from the nonce field of the parent header
// unless the field is zero, in which case we use the Canyon values.
if parent.Nonce != types.BlockNonce([8]byte{}) {
elasticity, denominator = DecodeHolocene1559Params(parent.Nonce)
}
}

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
44 changes: 44 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,47 @@ 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 a
// zero nonce, 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) {
elasticity2Denom10Nonce := EncodeHolocene1559Params(2, 10)
elasticity10Denom2Nonce := EncodeHolocene1559Params(10, 2)
parentBaseFee := int64(10_000_000)
parentGasLimit := uint64(30_000_000)

tests := []struct {
parentGasUsed uint64
expectedBaseFee int64
nonce types.BlockNonce
}{
{parentGasLimit / 2, parentBaseFee, elasticity2Denom10Nonce}, // target
{10_000_000, 9_666_667, elasticity2Denom10Nonce}, // below
{20_000_000, 10_333_333, elasticity2Denom10Nonce}, // above
{parentGasLimit / 10, parentBaseFee, elasticity10Denom2Nonce}, // target
{1_000_000, 6_666_667, elasticity10Denom2Nonce}, // below
{30_000_000, 55_000_000, elasticity10Denom2Nonce}, // above
}
for i, test := range tests {
parent := &types.Header{
Number: common.Big32,
GasLimit: parentGasLimit,
GasUsed: test.parentGasUsed,
BaseFee: big.NewInt(parentBaseFee),
Time: 10,
Nonce: test.nonce,
}
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