diff --git a/consensus/misc/eip1559/eip1559.go b/consensus/misc/eip1559/eip1559.go index 84b82c4c49..8c50c52b00 100644 --- a/consensus/misc/eip1559/eip1559.go +++ b/consensus/misc/eip1559/eip1559.go @@ -35,7 +35,7 @@ func VerifyEIP1559Header(config *params.ChainConfig, parent, header *types.Heade // Verify that the gas limit remains within allowed bounds parentGasLimit := parent.GasLimit if !config.IsLondon(parent.Number) { - parentGasLimit = parent.GasLimit * config.ElasticityMultiplier() + parentGasLimit = parent.GasLimit * config.ElasticityMultiplier(parent.Number.Uint64()) } if err := misc.VerifyGaslimit(parentGasLimit, header.GasLimit); err != nil { return err @@ -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 := parent.GasLimit / config.ElasticityMultiplier(parent.Number.Uint64()+1) // 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) @@ -77,7 +77,7 @@ func CalcBaseFee(config *params.ChainConfig, parent *types.Header) *big.Int { num.SetUint64(parent.GasUsed - parentGasTarget) num.Mul(num, parent.BaseFee) num.Div(num, denom.SetUint64(parentGasTarget)) - num.Div(num, denom.SetUint64(config.BaseFeeChangeDenominator())) + num.Div(num, denom.SetUint64(config.BaseFeeChangeDenominator(parent.Number.Uint64()+1))) baseFeeDelta := math.BigMax(num, common.Big1) return num.Add(parent.BaseFee, baseFeeDelta) @@ -87,9 +87,14 @@ func CalcBaseFee(config *params.ChainConfig, parent *types.Header) *big.Int { num.SetUint64(parentGasTarget - parent.GasUsed) num.Mul(num, parent.BaseFee) num.Div(num, denom.SetUint64(parentGasTarget)) - num.Div(num, denom.SetUint64(config.BaseFeeChangeDenominator())) + num.Div(num, denom.SetUint64(config.BaseFeeChangeDenominator(parent.Number.Uint64()+1))) baseFee := num.Sub(parent.BaseFee, num) - return math.BigMax(baseFee, common.Big0) + lowerBound := common.Big0 + if config.AstriaEIP1559Params != nil { + lowerBound = config.AstriaEIP1559Params.MinBaseFeeAt(parent.Number.Uint64() + 1) + } + + return math.BigMax(baseFee, lowerBound) } } diff --git a/core/chain_makers.go b/core/chain_makers.go index 31c111b73e..fe5ad18ca1 100644 --- a/core/chain_makers.go +++ b/core/chain_makers.go @@ -231,7 +231,7 @@ func (b *BlockGen) AddUncle(h *types.Header) { if b.cm.config.IsLondon(h.Number) { h.BaseFee = eip1559.CalcBaseFee(b.cm.config, parent) if !b.cm.config.IsLondon(parent.Number) { - parentGasLimit := parent.GasLimit * b.cm.config.ElasticityMultiplier() + parentGasLimit := parent.GasLimit * b.cm.config.ElasticityMultiplier(parent.Number.Uint64()) h.GasLimit = CalcGasLimit(parentGasLimit, parentGasLimit) } } @@ -431,7 +431,7 @@ func (cm *chainMaker) makeHeader(parent *types.Block, state *state.StateDB, engi if cm.config.IsLondon(header.Number) { header.BaseFee = eip1559.CalcBaseFee(cm.config, parent.Header()) if !cm.config.IsLondon(parent.Number()) { - parentGasLimit := parent.GasLimit() * cm.config.ElasticityMultiplier() + parentGasLimit := parent.GasLimit() * cm.config.ElasticityMultiplier(parent.Number().Uint64()) header.GasLimit = CalcGasLimit(parentGasLimit, parentGasLimit) } } diff --git a/genesis.json b/genesis.json index 6e5bd398da..4298c0d6d2 100644 --- a/genesis.json +++ b/genesis.json @@ -30,6 +30,9 @@ ], "astriaFeeCollectors": { "1": "0xaC21B97d35Bf75A7dAb16f35b111a50e78A72F30" + }, + "astriaEIP1559Params": { + "1": { "minBaseFee": 0, "elasticityMultiplier": 2, "BaseFeeChangeDenominator": 8 } } }, "difficulty": "10000000", diff --git a/miner/worker.go b/miner/worker.go index 58b53a8b5a..e77c135b5f 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -1025,7 +1025,7 @@ func (w *worker) prepareWork(genParams *generateParams) (*environment, error) { if w.chainConfig.IsLondon(header.Number) { header.BaseFee = eip1559.CalcBaseFee(w.chainConfig, parent) if !w.chainConfig.IsLondon(parent.Number) { - parentGasLimit := parent.GasLimit * w.chainConfig.ElasticityMultiplier() + parentGasLimit := parent.GasLimit * w.chainConfig.ElasticityMultiplier(parent.Number.Uint64()) header.GasLimit = core.CalcGasLimit(parentGasLimit, w.config.GasCeil) } } diff --git a/params/astria_config_test.go b/params/astria_config_test.go new file mode 100644 index 0000000000..f2b100a875 --- /dev/null +++ b/params/astria_config_test.go @@ -0,0 +1,97 @@ +package params + +import ( + "encoding/json" + "math/big" + "reflect" + "testing" + + "github.com/ethereum/go-ethereum/common" +) + +func TestAstriaEIP1559Params(t *testing.T) { + jsonBuf := []byte(`{ + "1":{ "minBaseFee": 45000000000, "elasticityMultiplier": 4, "baseFeeChangeDenominator": 100 }, + "101":{ "minBaseFee": 120000000, "elasticityMultiplier": 11, "baseFeeChangeDenominator": 250 }, + "15":{ "minBaseFee": 15000000000, "elasticityMultiplier": 5, "baseFeeChangeDenominator": 50 } + }`) + + var eip1559Params AstriaEIP1559Params + err := json.Unmarshal(jsonBuf, &eip1559Params) + if err != nil { + t.Errorf("unexpected err %v", err) + } + + expected := AstriaEIP1559Params{ + heights: map[uint64]AstriaEIP1559Param{ + 1: {MinBaseFee: 45000000000, ElasticityMultiplier: 4, BaseFeeChangeDenominator: 100}, + 101: {MinBaseFee: 120000000, ElasticityMultiplier: 11, BaseFeeChangeDenominator: 250}, + 15: {MinBaseFee: 15000000000, ElasticityMultiplier: 5, BaseFeeChangeDenominator: 50}, + }, + orderedHeights: []uint64{101, 15, 1}, + } + + if !reflect.DeepEqual(eip1559Params, expected) { + t.Errorf("expected %v, got %v", expected, eip1559Params) + } + + minBaseTests := map[uint64]*big.Int{ + 0: common.Big0, + 1: big.NewInt(45000000000), + 2: big.NewInt(45000000000), + 14: big.NewInt(45000000000), + 15: big.NewInt(15000000000), + 16: big.NewInt(15000000000), + 50: big.NewInt(15000000000), + 100: big.NewInt(15000000000), + 101: big.NewInt(120000000), + 102: big.NewInt(120000000), + 123456: big.NewInt(120000000), + } + + for height, expected := range minBaseTests { + if got := eip1559Params.MinBaseFeeAt(height); got.Cmp(expected) != 0 { + t.Errorf("MinBaseFeeAt(%d): expected %v, got %v", height, expected, got) + } + } + + elasticityMultiplierTests := map[uint64]uint64{ + 0: DefaultElasticityMultiplier, + 1: 4, + 2: 4, + 14: 4, + 15: 5, + 16: 5, + 50: 5, + 100: 5, + 101: 11, + 102: 11, + 123456: 11, + } + + for height, expected := range elasticityMultiplierTests { + if got := eip1559Params.ElasticityMultiplierAt(height); got != expected { + t.Errorf("ElasticityMultiplierAt(%d): expected %v, got %v", height, expected, got) + } + } + + baseFeeChangeDenominatorTests := map[uint64]uint64{ + 0: DefaultBaseFeeChangeDenominator, + 1: 100, + 2: 100, + 14: 100, + 15: 50, + 16: 50, + 50: 50, + 100: 50, + 101: 250, + 102: 250, + 123456: 250, + } + + for height, expected := range baseFeeChangeDenominatorTests { + if got := eip1559Params.BaseFeeChangeDenominatorAt(height); got != expected { + t.Errorf("BaseFeeChangeDenominatorAt(%d): expected %v, got %v", height, expected, got) + } + } +} diff --git a/params/config.go b/params/config.go index 35e5c72fe9..fd8924bfe5 100644 --- a/params/config.go +++ b/params/config.go @@ -17,8 +17,10 @@ package params import ( + "encoding/json" "fmt" "math/big" + "sort" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" @@ -345,6 +347,7 @@ type ChainConfig struct { AstriaCelestiaHeightVariance uint32 `json:"astriaCelestiaHeightVariance,omitempty"` AstriaBridgeAddressConfigs []AstriaBridgeAddressConfig `json:"astriaBridgeAddresses,omitempty"` AstriaFeeCollectors map[uint32]common.Address `json:"astriaFeeCollectors"` + AstriaEIP1559Params *AstriaEIP1559Params `json:"astriaEIP1559Params,omitempty"` } func (c *ChainConfig) AstriaExtraData() []byte { @@ -366,6 +369,70 @@ func (c *ChainConfig) AstriaExtraData() []byte { return extra } +type AstriaEIP1559Param struct { + MinBaseFee uint64 `json:"minBaseFee"` + ElasticityMultiplier uint64 `json:"elasticityMultiplier"` + BaseFeeChangeDenominator uint64 `json:"baseFeeChangeDenominator"` +} + +type AstriaEIP1559Params struct { + heights map[uint64]AstriaEIP1559Param + orderedHeights []uint64 +} + +func NewAstriaEIP1559Params(heights map[uint64]AstriaEIP1559Param) *AstriaEIP1559Params { + orderedHeights := []uint64{} + for k := range heights { + orderedHeights = append(orderedHeights, k) + } + sort.Slice(orderedHeights, func(i, j int) bool { return orderedHeights[i] > orderedHeights[j] }) + + return &AstriaEIP1559Params{ + heights: heights, + orderedHeights: orderedHeights, + } +} + +func (c *AstriaEIP1559Params) MinBaseFeeAt(height uint64) *big.Int { + for _, h := range c.orderedHeights { + if height >= h { + return big.NewInt(0).SetUint64(c.heights[h].MinBaseFee) + } + } + return common.Big0 +} + +func (c *AstriaEIP1559Params) ElasticityMultiplierAt(height uint64) uint64 { + for _, h := range c.orderedHeights { + if height >= h { + return c.heights[h].ElasticityMultiplier + } + } + return DefaultElasticityMultiplier +} + +func (c *AstriaEIP1559Params) BaseFeeChangeDenominatorAt(height uint64) uint64 { + for _, h := range c.orderedHeights { + if height >= h { + return c.heights[h].BaseFeeChangeDenominator + } + } + return DefaultBaseFeeChangeDenominator +} + +func (c AstriaEIP1559Params) MarshalJSON() ([]byte, error) { + return json.Marshal(c.heights) +} + +func (c *AstriaEIP1559Params) UnmarshalJSON(data []byte) error { + var heights map[uint64]AstriaEIP1559Param + if err := json.Unmarshal(data, &heights); err != nil { + return err + } + *c = *NewAstriaEIP1559Params(heights) + return nil +} + // EthashConfig is the consensus engine configs for proof-of-work based sealing. type EthashConfig struct{} @@ -742,12 +809,18 @@ func (c *ChainConfig) checkCompatible(newcfg *ChainConfig, headNumber *big.Int, } // BaseFeeChangeDenominator bounds the amount the base fee can change between blocks. -func (c *ChainConfig) BaseFeeChangeDenominator() uint64 { +func (c *ChainConfig) BaseFeeChangeDenominator(height uint64) uint64 { + if c.AstriaEIP1559Params != nil { + return c.AstriaEIP1559Params.BaseFeeChangeDenominatorAt(height) + } return DefaultBaseFeeChangeDenominator } // ElasticityMultiplier bounds the maximum gas limit an EIP-1559 block may have. -func (c *ChainConfig) ElasticityMultiplier() uint64 { +func (c *ChainConfig) ElasticityMultiplier(height uint64) uint64 { + if c.AstriaEIP1559Params != nil { + return c.AstriaEIP1559Params.ElasticityMultiplierAt(height) + } return DefaultElasticityMultiplier }