diff --git a/consensus/misc/eip1559.go b/consensus/misc/eip1559.go index 9990df04a7d1..c20b627aa212 100644 --- a/consensus/misc/eip1559.go +++ b/consensus/misc/eip1559.go @@ -102,13 +102,87 @@ func VerifyEip1559Header(config *params.ChainConfig, parent, header *types.Heade } // CalcBaseFee calculates the basefee of the header. -func CalcBaseFee(config *params.ChainConfig, parent *types.Header, parentL1BaseFee *big.Int) *big.Int { +func CalcBaseFee(config *params.ChainConfig, parent *types.Header, parentL1BaseFee *big.Int, currentHeaderTime uint64) *big.Int { if config.Clique != nil && config.Clique.ShadowForkHeight != 0 && parent.Number.Uint64() >= config.Clique.ShadowForkHeight { return big.NewInt(10000000) // 0.01 Gwei } scalar, overhead := ReadL2BaseFeeCoefficients() - return calcBaseFee(scalar, overhead, parentL1BaseFee) + + if parent == nil || parent.Number == nil || !config.IsFeynman(currentHeaderTime) { + return calcBaseFee(scalar, overhead, parentL1BaseFee) + } + // In Feynman base fee calculation, we reuse the contract's baseFeeOverhead slot as the proving base fee. + return calcBaseFeeFeynman(config, parent, overhead) +} + +// calcBaseFeeFeynman calculates the basefee of the header for Feynman fork. +func calcBaseFeeFeynman(config *params.ChainConfig, parent *types.Header, overhead *big.Int) *big.Int { + baseFeeEIP1559 := calcBaseFeeEIP1559(config, parent) + baseFee := new(big.Int).Set(baseFeeEIP1559) + baseFee.Add(baseFee, overhead) + + return baseFee +} + +// CalcBaseFee calculates the basefee of the header. +func calcBaseFeeEIP1559(config *params.ChainConfig, parent *types.Header) *big.Int { + // If the current block is the first EIP-1559 block, return the InitialBaseFee. + if !config.IsFeynman(parent.Time) { + // If the parent block is not nil, return its base fee to make fee transition smooth. + if parent.BaseFee != nil { + return new(big.Int).Set(parent.BaseFee) + } + return new(big.Int).SetUint64(params.InitialBaseFee) + } + + parentBaseFeeEIP1559 := extractBaseFeeEIP1559(config, parent.BaseFee) + parentGasTarget := parent.GasLimit / config.ElasticityMultiplier() + // If the parent gasUsed is the same as the target, the baseFee remains unchanged. + if parent.GasUsed == parentGasTarget { + return new(big.Int).Set(parentBaseFeeEIP1559) + } + + var ( + num = new(big.Int) + denom = new(big.Int) + ) + + if parent.GasUsed > parentGasTarget { + // If the parent block used more gas than its target, the baseFee should increase. + // max(1, parentBaseFee * gasUsedDelta / parentGasTarget / baseFeeChangeDenominator) + num.SetUint64(parent.GasUsed - parentGasTarget) + num.Mul(num, parentBaseFeeEIP1559) + num.Div(num, denom.SetUint64(parentGasTarget)) + num.Div(num, denom.SetUint64(config.BaseFeeChangeDenominator())) + if num.Cmp(common.Big1) < 0 { + return num.Add(parentBaseFeeEIP1559, common.Big1) + } + baseFee := num.Add(parentBaseFeeEIP1559, num) + if baseFee.Cmp(big.NewInt(MaximumL2BaseFee)) > 0 { + baseFee = big.NewInt(MaximumL2BaseFee) + } + return baseFee + } else { + // Otherwise if the parent block used less gas than its target, the baseFee should decrease. + // max(0, parentBaseFee * gasUsedDelta / parentGasTarget / baseFeeChangeDenominator) + num.SetUint64(parentGasTarget - parent.GasUsed) + num.Mul(num, parentBaseFeeEIP1559) + num.Div(num, denom.SetUint64(parentGasTarget)) + num.Div(num, denom.SetUint64(config.BaseFeeChangeDenominator())) + + baseFee := num.Sub(parentBaseFeeEIP1559, num) + if baseFee.Cmp(common.Big0) < 0 { + baseFee = common.Big0 + } + return baseFee + } +} + +func extractBaseFeeEIP1559(config *params.ChainConfig, baseFee *big.Int) *big.Int { + _, overhead := ReadL2BaseFeeCoefficients() + // In Feynman base fee calculation, we reuse the contract's baseFeeOverhead slot as the proving base fee. + return new(big.Int).Sub(baseFee, overhead) } // MinBaseFee calculates the minimum L2 base fee based on the current coefficients. diff --git a/consensus/misc/eip1559_test.go b/consensus/misc/eip1559_test.go index d41d3e59666f..6422e99e2d71 100644 --- a/consensus/misc/eip1559_test.go +++ b/consensus/misc/eip1559_test.go @@ -20,6 +20,7 @@ import ( "math/big" "testing" + "github.com/scroll-tech/go-ethereum/common" "github.com/scroll-tech/go-ethereum/core/types" "github.com/scroll-tech/go-ethereum/params" ) @@ -124,7 +125,33 @@ func TestCalcBaseFee(t *testing.T) { config := config() UpdateL2BaseFeeScalar(big.NewInt(10000000)) UpdateL2BaseFeeOverhead(big.NewInt(1)) - if have, want := CalcBaseFee(config, nil, big.NewInt(test.parentL1BaseFee)), big.NewInt(test.expectedL2BaseFee); have.Cmp(want) != 0 { + if have, want := CalcBaseFee(config, nil, big.NewInt(test.parentL1BaseFee), 0), big.NewInt(test.expectedL2BaseFee); have.Cmp(want) != 0 { + t.Errorf("test %d: have %d want %d, ", i, have, want) + } + } + + tests1559 := []struct { + parentBaseFee int64 + parentGasLimit uint64 + parentGasUsed uint64 + expectedBaseFee int64 + }{ + {1000000000, 20000000, 10000000, 1000000000}, // usage == target + {1000000001, 20000000, 9000000, 987500001}, // usage below target + {1000000001, 20000000, 11000000, 1012500001}, // usage above target + } + for i, test := range tests1559 { + parent := &types.Header{ + Number: common.Big32, + GasLimit: test.parentGasLimit, + GasUsed: test.parentGasUsed, + BaseFee: big.NewInt(test.parentBaseFee), + } + config := config() + UpdateL2BaseFeeOverhead(big.NewInt(1)) + var feynmanTime uint64 + config.FeynmanTime = &feynmanTime + if have, want := CalcBaseFee(config, parent, big.NewInt(1), 1), big.NewInt(test.expectedBaseFee); have.Cmp(want) != 0 { t.Errorf("test %d: have %d want %d, ", i, have, want) } } @@ -144,7 +171,7 @@ func TestCalcBaseFee(t *testing.T) { for i, test := range testsWithDefaults { UpdateL2BaseFeeScalar(big.NewInt(34000000000000)) UpdateL2BaseFeeOverhead(big.NewInt(15680000)) - if have, want := CalcBaseFee(config(), nil, big.NewInt(test.parentL1BaseFee)), big.NewInt(test.expectedL2BaseFee); have.Cmp(want) != 0 { + if have, want := CalcBaseFee(config(), nil, big.NewInt(test.parentL1BaseFee), 0), big.NewInt(test.expectedL2BaseFee); have.Cmp(want) != 0 { t.Errorf("test %d: have %d want %d, ", i, have, want) } } diff --git a/core/chain_makers.go b/core/chain_makers.go index b37ed9c3eeec..f4b9418111e0 100644 --- a/core/chain_makers.go +++ b/core/chain_makers.go @@ -308,7 +308,7 @@ func makeHeader(chain consensus.ChainReader, parent *types.Block, state *state.S } if chain.Config().IsCurie(header.Number) { parentL1BaseFee := fees.GetL1BaseFee(state) - header.BaseFee = misc.CalcBaseFee(chain.Config(), parent.Header(), parentL1BaseFee) + header.BaseFee = misc.CalcBaseFee(chain.Config(), parent.Header(), parentL1BaseFee, header.Time) } return header } diff --git a/core/genesis.go b/core/genesis.go index a5eea6f30c3f..f044217ff4b7 100644 --- a/core/genesis.go +++ b/core/genesis.go @@ -317,7 +317,7 @@ func (g *Genesis) ToBlock(db ethdb.Database) *types.Block { if g.BaseFee != nil { head.BaseFee = g.BaseFee } else { - head.BaseFee = misc.CalcBaseFee(g.Config, nil, big.NewInt(0)) + head.BaseFee = misc.CalcBaseFee(g.Config, nil, big.NewInt(0), head.Time) } } statedb.Commit(false) diff --git a/core/state_processor_test.go b/core/state_processor_test.go index 7fbc812cb6d2..e3a2f96e3e69 100644 --- a/core/state_processor_test.go +++ b/core/state_processor_test.go @@ -241,7 +241,7 @@ func TestStateProcessorErrors(t *testing.T) { txs: []*types.Transaction{ mkSetCodeTx(0, common.Address{}, params.TxGas, big.NewInt(params.InitialBaseFee), big.NewInt(params.InitialBaseFee), nil), }, - want: "could not apply tx 0 [0xc18d10f4c809dbdfa1a074c3300de9bc4b7f16a20f0ec667f6f67312b71b956a]: EIP-7702 transaction with empty auth list (sender 0x71562b71999873DB5b286dF957af199Ec94617F7)", + want: "could not apply tx 0 [0xa230ea82ab24a8e60aca9edfe901bab53c92d0d8850f13c3f72513608229e2f3]: EIP-7702 transaction with empty auth list (sender 0x71562b71999873DB5b286dF957af199Ec94617F7)", }, // ErrSetCodeTxCreate cannot be tested here: it is impossible to create a SetCode-tx with nil `to`. // The EstimateGas API tests test this case. @@ -391,13 +391,13 @@ func TestStateProcessorErrors(t *testing.T) { }{ { // ErrMaxInitCodeSizeExceeded txs: []*types.Transaction{ - mkDynamicCreationTx(0, 520000, common.Big0, misc.CalcBaseFee(config, genesis.Header(), parentL1BaseFee), tooBigInitCode[:]), + mkDynamicCreationTx(0, 520000, common.Big0, misc.CalcBaseFee(config, genesis.Header(), parentL1BaseFee, 0), tooBigInitCode[:]), }, want: "could not apply tx 0 [0xe0d03426cecc04467410064cb4de02012fc069d2462282735d7dfcb9dea9f63b]: max initcode size exceeded: code size 49153 limit 49152", }, { // ErrIntrinsicGas: Not enough gas to cover init code txs: []*types.Transaction{ - mkDynamicCreationTx(0, 54299, common.Big0, misc.CalcBaseFee(config, genesis.Header(), parentL1BaseFee), smallInitCode[:]), + mkDynamicCreationTx(0, 54299, common.Big0, misc.CalcBaseFee(config, genesis.Header(), parentL1BaseFee, 0), smallInitCode[:]), }, want: "could not apply tx 0 [0x98e54c5ecfa7986a66480d65ba32f2c6a2a6aedc3a67abb91b1e118b0717ed2d]: intrinsic gas too low: have 54299, want 54300", }, @@ -436,7 +436,7 @@ func GenerateBadBlock(parent *types.Block, engine consensus.Engine, txs types.Tr if config.IsCurie(header.Number) { parentL1BaseFee := big.NewInt(1000000000) // 1 gwei - header.BaseFee = misc.CalcBaseFee(config, parent.Header(), parentL1BaseFee) + header.BaseFee = misc.CalcBaseFee(config, parent.Header(), parentL1BaseFee, header.Time) } var receipts []*types.Receipt // The post-state result doesn't need to be correct (this is a bad block), but we do need something there diff --git a/core/tx_pool.go b/core/tx_pool.go index 52f4b040d42c..2c8650d573f4 100644 --- a/core/tx_pool.go +++ b/core/tx_pool.go @@ -1459,7 +1459,8 @@ func (pool *TxPool) runReorg(done chan struct{}, reset *txpoolResetRequest, dirt pool.demoteUnexecutables() if reset.newHead != nil && pool.chainconfig.IsCurie(new(big.Int).Add(reset.newHead.Number, big.NewInt(1))) { l1BaseFee := fees.GetL1BaseFee(pool.currentState) - pendingBaseFee := misc.CalcBaseFee(pool.chainconfig, reset.newHead, l1BaseFee) + // Use time.Now() as the current block time to calculate the pending base fee. + pendingBaseFee := misc.CalcBaseFee(pool.chainconfig, reset.newHead, l1BaseFee, uint64(time.Now().Unix())) pool.priced.SetBaseFee(pendingBaseFee) } // Update all accounts to the latest known pending nonce diff --git a/eth/catalyst/api.go b/eth/catalyst/api.go index 3bc685b8674f..1d0da17f5872 100644 --- a/eth/catalyst/api.go +++ b/eth/catalyst/api.go @@ -148,7 +148,7 @@ func (api *consensusAPI) AssembleBlock(params assembleBlockParams) (*executableD return nil, err } parentL1BaseFee := fees.GetL1BaseFee(stateDb) - header.BaseFee = misc.CalcBaseFee(config, parent.Header(), parentL1BaseFee) + header.BaseFee = misc.CalcBaseFee(config, parent.Header(), parentL1BaseFee, header.Time) } err = api.eth.Engine().Prepare(bc, header, nil) if err != nil { @@ -274,7 +274,7 @@ func insertBlockParamsToBlock(config *chainParams.ChainConfig, parent *types.Hea Time: params.Timestamp, } if config.IsCurie(number) { - header.BaseFee = misc.CalcBaseFee(config, parent, parentL1BaseFee) + header.BaseFee = misc.CalcBaseFee(config, parent, parentL1BaseFee, header.Time) } block := types.NewBlockWithHeader(header).WithBody(txs, nil /* uncles */) return block, nil diff --git a/eth/gasprice/feehistory.go b/eth/gasprice/feehistory.go index 571dfa3d7448..6b4b2f72fccc 100644 --- a/eth/gasprice/feehistory.go +++ b/eth/gasprice/feehistory.go @@ -96,7 +96,8 @@ func (oracle *Oracle) processBlock(bf *blockFees, percentiles []float64, nonCong return } l1BaseFee := fees.GetL1BaseFee(state) - bf.results.nextBaseFee = misc.CalcBaseFee(chainconfig, bf.header, l1BaseFee) + // Use bf.header.Time as the current block time to calculate the next base fee because there are cases to query historical block fees + bf.results.nextBaseFee = misc.CalcBaseFee(chainconfig, bf.header, l1BaseFee, bf.header.Time) } else { bf.results.nextBaseFee = new(big.Int) } diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index f41090a8c42d..7763f76abeb1 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -1447,7 +1447,8 @@ func newRPCPendingTransaction(tx *types.Transaction, current *types.Header, conf blockNumber := uint64(0) blockTime := uint64(0) if current != nil { - baseFee = misc.CalcBaseFee(config, current, l1BaseFee) + // Use time.Now() as the current block time to calculate the pending base fee. + baseFee = misc.CalcBaseFee(config, current, l1BaseFee, uint64(time.Now().Unix())) blockNumber = current.Number.Uint64() blockTime = current.Time } diff --git a/miner/scroll_worker.go b/miner/scroll_worker.go index da1a67cd092b..a1be4e82b143 100644 --- a/miner/scroll_worker.go +++ b/miner/scroll_worker.go @@ -488,7 +488,7 @@ func (w *worker) newWork(now time.Time, parent *types.Block, reorging bool, reor // Set baseFee if we are on an EIP-1559 chain if w.chainConfig.IsCurie(header.Number) { parentL1BaseFee := fees.GetL1BaseFee(parentState) - header.BaseFee = misc.CalcBaseFee(w.chainConfig, parent.Header(), parentL1BaseFee) + header.BaseFee = misc.CalcBaseFee(w.chainConfig, parent.Header(), parentL1BaseFee, header.Time) } // Only set the coinbase if our consensus engine is running (avoid spurious block rewards) if w.isRunning() { diff --git a/params/config.go b/params/config.go index d859f6384dea..8d6d50ed5045 100644 --- a/params/config.go +++ b/params/config.go @@ -1033,6 +1033,16 @@ func (c *ChainConfig) CheckCompatible(newcfg *ChainConfig, height uint64) *Confi return lasterr } +// BaseFeeChangeDenominator bounds the amount the base fee can change between blocks. +func (c *ChainConfig) BaseFeeChangeDenominator() uint64 { + return DefaultBaseFeeChangeDenominator +} + +// ElasticityMultiplier bounds the maximum gas limit an EIP-1559 block may have. +func (c *ChainConfig) ElasticityMultiplier() uint64 { + return DefaultElasticityMultiplier +} + // CheckConfigForkOrder checks that we don't "skip" any forks, geth isn't pluggable enough // to guarantee that forks can be implemented in a different order than on official networks func (c *ChainConfig) CheckConfigForkOrder() error { diff --git a/params/protocol_params.go b/params/protocol_params.go index 7fa848db8305..d3250d0dab9c 100644 --- a/params/protocol_params.go +++ b/params/protocol_params.go @@ -127,9 +127,9 @@ const ( // Introduced in Tangerine Whistle (Eip 150) CreateBySelfdestructGas uint64 = 25000 - BaseFeeChangeDenominator = 8 // Bounds the amount the base fee can change between blocks. - ElasticityMultiplier = 2 // Bounds the maximum gas limit an EIP-1559 block may have. - InitialBaseFee = 1000000000 // Initial base fee for EIP-1559 blocks. + 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. + InitialBaseFee = 10000000 // Initial base fee for EIP-1559 blocks. MaxCodeSize = 24576 // Maximum bytecode to permit for a contract MaxInitCodeSize = 2 * MaxCodeSize // Maximum initcode to permit in a creation transaction and create instructions diff --git a/params/version.go b/params/version.go index d0d69aa49164..897b9f4262b0 100644 --- a/params/version.go +++ b/params/version.go @@ -24,7 +24,7 @@ import ( const ( VersionMajor = 5 // Major version component of the current release VersionMinor = 8 // Minor version component of the current release - VersionPatch = 60 // Patch version component of the current release + VersionPatch = 61 // Patch version component of the current release VersionMeta = "mainnet" // Version metadata to append to the version string )