From ccec84ce63c8e768f92ad0bb5249a05e3daa0a9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Garamv=C3=B6lgyi?= Date: Mon, 11 Mar 2024 13:57:52 +0000 Subject: [PATCH] feat: re-enable EIP-1559 in Banach hard fork (#634) * feat: re-enable EIP-1559 in Banach hard fork * enable BASEFEE opcode * do not double gas limit on Banach fork block * do not burn base fee * update base fee calculation logic * fix gas price oracle * fix ethapi backend * typo * reorder worker code * update genesis base fee * add MaximumL2BaseFee * add base fee metrics * handle nil base fee * revert state-transition change * bump version * remove TODO * update test * fix typo * fix typo * update traces * fix test * update london to banach * handle error * bump version * bump version --- accounts/abi/bind/backends/simulated.go | 2 +- accounts/abi/bind/base.go | 2 +- cmd/evm/internal/t8ntool/transition.go | 9 +-- consensus/clique/clique.go | 2 +- consensus/ethash/consensus.go | 2 +- consensus/misc/eip1559.go | 89 ++++++++----------------- consensus/misc/eip1559_test.go | 41 +++++------- core/blockchain.go | 9 +++ core/blockchain_test.go | 8 ++- core/chain_makers.go | 10 ++- core/genesis.go | 7 +- core/state_processor_test.go | 17 +++-- core/state_transition.go | 11 ++- core/tx_pool.go | 9 +-- core/types/l2trace.go | 62 +++++++++-------- core/types/transaction_signing.go | 2 + core/vm/interpreter.go | 2 + core/vm/jump_table.go | 9 +++ eth/api_backend.go | 4 ++ eth/catalyst/api.go | 23 +++++-- eth/gasprice/feehistory.go | 20 ++++-- eth/gasprice/gasprice.go | 2 + eth/gasprice/gasprice_test.go | 8 +++ internal/ethapi/api.go | 70 ++++++++++++++----- internal/ethapi/backend.go | 1 + internal/ethapi/transaction_args.go | 10 +-- les/api_backend.go | 4 ++ miner/worker.go | 22 +++--- params/config.go | 36 ++-------- params/version.go | 2 +- rollup/fees/rollup_fee.go | 4 ++ tests/state_test_util.go | 2 +- 32 files changed, 272 insertions(+), 229 deletions(-) diff --git a/accounts/abi/bind/backends/simulated.go b/accounts/abi/bind/backends/simulated.go index f6919557fc30..26c8307ce61b 100644 --- a/accounts/abi/bind/backends/simulated.go +++ b/accounts/abi/bind/backends/simulated.go @@ -590,7 +590,7 @@ func (b *SimulatedBackend) callContract(ctx context.Context, call ethereum.CallM return nil, errors.New("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified") } head := b.blockchain.CurrentHeader() - if !b.blockchain.Config().IsLondon(head.Number) { + if !b.blockchain.Config().IsBanach(head.Number) { // If there's no basefee, then it must be a non-1559 execution if call.GasPrice == nil { call.GasPrice = new(big.Int) diff --git a/accounts/abi/bind/base.go b/accounts/abi/bind/base.go index b6345e0b207b..9f388f0b7c67 100644 --- a/accounts/abi/bind/base.go +++ b/accounts/abi/bind/base.go @@ -289,7 +289,7 @@ func (c *BoundContract) createDynamicTx(opts *TransactOpts, contract *common.Add func (c *BoundContract) createLegacyTx(opts *TransactOpts, contract *common.Address, input []byte) (*types.Transaction, error) { if opts.GasFeeCap != nil || opts.GasTipCap != nil { - return nil, errors.New("maxFeePerGas or maxPriorityFeePerGas specified but london is not active yet") + return nil, errors.New("maxFeePerGas or maxPriorityFeePerGas specified but banach is not active yet") } // Normalize value value := opts.Value diff --git a/cmd/evm/internal/t8ntool/transition.go b/cmd/evm/internal/t8ntool/transition.go index 8520dfb7f1b5..e19280610f60 100644 --- a/cmd/evm/internal/t8ntool/transition.go +++ b/cmd/evm/internal/t8ntool/transition.go @@ -247,8 +247,8 @@ func Transition(ctx *cli.Context) error { return NewError(ErrorJson, fmt.Errorf("failed signing transactions: %v", err)) } // Sanity check, to not `panic` in state_transition - if chainConfig.IsLondon(big.NewInt(int64(prestate.Env.Number))) { - if prestate.Env.BaseFee == nil && chainConfig.Scroll.BaseFeeEnabled() { + if chainConfig.IsBanach(big.NewInt(int64(prestate.Env.Number))) { + if prestate.Env.BaseFee == nil { return NewError(ErrorConfig, errors.New("EIP-1559 config but missing 'currentBaseFee' in env section")) } } @@ -321,8 +321,9 @@ func (t *txWithKey) UnmarshalJSON(input []byte) error { // signUnsignedTransactions converts the input txs to canonical transactions. // // The transactions can have two forms, either -// 1. unsigned or -// 2. signed +// 1. unsigned or +// 2. signed +// // For (1), r, s, v, need so be zero, and the `secretKey` needs to be set. // If so, we sign it here and now, with the given `secretKey` // If the condition above is not met, then it's considered a signed transaction. diff --git a/consensus/clique/clique.go b/consensus/clique/clique.go index 702b029d52e5..33ccb82a7a86 100644 --- a/consensus/clique/clique.go +++ b/consensus/clique/clique.go @@ -335,7 +335,7 @@ func (c *Clique) verifyCascadingFields(chain consensus.ChainHeaderReader, header if header.GasUsed > header.GasLimit { return fmt.Errorf("invalid gasUsed: have %d, gasLimit %d", header.GasUsed, header.GasLimit) } - if !chain.Config().IsLondon(header.Number) { + if !chain.Config().IsBanach(header.Number) { // Verify BaseFee not present before EIP-1559 fork. if header.BaseFee != nil { return fmt.Errorf("invalid baseFee before fork: have %d, want ", header.BaseFee) diff --git a/consensus/ethash/consensus.go b/consensus/ethash/consensus.go index c7478b8fb76f..78154d4dac0e 100644 --- a/consensus/ethash/consensus.go +++ b/consensus/ethash/consensus.go @@ -291,7 +291,7 @@ func (ethash *Ethash) verifyHeader(chain consensus.ChainHeaderReader, header, pa return fmt.Errorf("invalid gasUsed: have %d, gasLimit %d", header.GasUsed, header.GasLimit) } // Verify the block's gas usage and (if applicable) verify the base fee. - if !chain.Config().IsLondon(header.Number) { + if !chain.Config().IsBanach(header.Number) { // Verify BaseFee not present before EIP-1559 fork. if header.BaseFee != nil { return fmt.Errorf("invalid baseFee before fork: have %d, expected 'nil'", header.BaseFee) diff --git a/consensus/misc/eip1559.go b/consensus/misc/eip1559.go index 3063af83c08d..9a17bba51488 100644 --- a/consensus/misc/eip1559.go +++ b/consensus/misc/eip1559.go @@ -20,90 +20,53 @@ import ( "fmt" "math/big" - "github.com/scroll-tech/go-ethereum/common" - "github.com/scroll-tech/go-ethereum/common/math" "github.com/scroll-tech/go-ethereum/core/types" "github.com/scroll-tech/go-ethereum/params" ) +// Protocol-enforced maximum L2 base fee. +// We would only go above this if L1 base fee hits 700 Gwei. +const MaximumL2BaseFee = 10000000000 + // VerifyEip1559Header verifies some header attributes which were changed in EIP-1559, // - gas limit check // - basefee check func VerifyEip1559Header(config *params.ChainConfig, parent, header *types.Header) error { // Verify that the gas limit remains within allowed bounds - parentGasLimit := parent.GasLimit - if !config.IsLondon(parent.Number) { - parentGasLimit = parent.GasLimit * params.ElasticityMultiplier - } - if err := VerifyGaslimit(parentGasLimit, header.GasLimit); err != nil { + if err := VerifyGaslimit(parent.GasLimit, header.GasLimit); err != nil { return err } // Verify the header is not malformed - if header.BaseFee == nil && config.Scroll.BaseFeeEnabled() { - return fmt.Errorf("header is missing baseFee") - } - // Now BaseFee can be nil, because !config.Scroll.BaseFeeEnabled() if header.BaseFee == nil { - return nil - } - // Verify the baseFee is correct based on the parent header. - - var expectedBaseFee *big.Int - - // compatible check with the logic in commitNewWork - if config.Clique == nil || config.Scroll.BaseFeeEnabled() { - expectedBaseFee = CalcBaseFee(config, parent) - } else { - expectedBaseFee = big.NewInt(0) + return fmt.Errorf("header is missing baseFee") } - - if header.BaseFee.Cmp(expectedBaseFee) != 0 { - return fmt.Errorf("invalid baseFee: have %s, want %s, parentBaseFee %s, parentGasUsed %d", - expectedBaseFee, header.BaseFee, parent.BaseFee, parent.GasUsed) + // note: we do not verify L2 base fee, the sequencer has the + // right to set any base fee below the maximum. L2 base fee + // is not subject to L2 consensus or zk verification. + if header.BaseFee.Cmp(big.NewInt(MaximumL2BaseFee)) > 0 { + return fmt.Errorf("invalid baseFee: have %s, maximum %d", header.BaseFee, MaximumL2BaseFee) } return nil } // CalcBaseFee calculates the basefee of the header. -func CalcBaseFee(config *params.ChainConfig, parent *types.Header) *big.Int { - // If the current block is the first EIP-1559 block, return the InitialBaseFee. - if !config.IsLondon(parent.Number) { - return new(big.Int).SetUint64(params.InitialBaseFee) - } +func CalcBaseFee(config *params.ChainConfig, parent *types.Header, parentL1BaseFee *big.Int) *big.Int { + l2SequencerFee := big.NewInt(10000000) // 0.01 Gwei + provingFee := big.NewInt(140000000) // 0.14 Gwei - var ( - parentGasTarget = parent.GasLimit / params.ElasticityMultiplier - parentGasTargetBig = new(big.Int).SetUint64(parentGasTarget) - baseFeeChangeDenominator = new(big.Int).SetUint64(params.BaseFeeChangeDenominator) - ) - if !config.Scroll.BaseFeeEnabled() { - return nil - } - // 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) - } - if parent.GasUsed > parentGasTarget { - // If the parent block used more gas than its target, the baseFee should increase. - gasUsedDelta := new(big.Int).SetUint64(parent.GasUsed - parentGasTarget) - x := new(big.Int).Mul(parent.BaseFee, gasUsedDelta) - y := x.Div(x, parentGasTargetBig) - baseFeeDelta := math.BigMax( - x.Div(y, baseFeeChangeDenominator), - common.Big1, - ) + // L1_base_fee * 0.014 + verificationFee := parentL1BaseFee + verificationFee = new(big.Int).Mul(verificationFee, big.NewInt(14)) + verificationFee = new(big.Int).Div(verificationFee, big.NewInt(1000)) - return x.Add(parent.BaseFee, baseFeeDelta) - } else { - // Otherwise if the parent block used less gas than its target, the baseFee should decrease. - gasUsedDelta := new(big.Int).SetUint64(parentGasTarget - parent.GasUsed) - x := new(big.Int).Mul(parent.BaseFee, gasUsedDelta) - y := x.Div(x, parentGasTargetBig) - baseFeeDelta := x.Div(y, baseFeeChangeDenominator) + baseFee := big.NewInt(0) + baseFee.Add(baseFee, l2SequencerFee) + baseFee.Add(baseFee, provingFee) + baseFee.Add(baseFee, verificationFee) - return math.BigMax( - x.Sub(parent.BaseFee, baseFeeDelta), - common.Big0, - ) + if baseFee.Cmp(big.NewInt(MaximumL2BaseFee)) > 0 { + baseFee = big.NewInt(MaximumL2BaseFee) } + + return baseFee } diff --git a/consensus/misc/eip1559_test.go b/consensus/misc/eip1559_test.go index 611576bdff80..8aa22c830fa5 100644 --- a/consensus/misc/eip1559_test.go +++ b/consensus/misc/eip1559_test.go @@ -20,7 +20,6 @@ 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" ) @@ -52,7 +51,8 @@ func copyConfig(original *params.ChainConfig) *params.ChainConfig { func config() *params.ChainConfig { config := copyConfig(params.TestChainConfig) - config.LondonBlock = big.NewInt(5) + config.LondonBlock = big.NewInt(3) + config.BanachBlock = big.NewInt(5) return config } @@ -67,13 +67,13 @@ func TestBlockGasLimits(t *testing.T) { gasLimit uint64 ok bool }{ - // Transitions from non-london to london - {10000000, 4, 20000000, true}, // No change - {10000000, 4, 20019530, true}, // Upper limit - {10000000, 4, 20019531, false}, // Upper +1 - {10000000, 4, 19980470, true}, // Lower limit - {10000000, 4, 19980469, false}, // Lower limit -1 - // London to London + // Transitions from non-banach to banach + {10000000, 4, 10000000, true}, // No change + {10000000, 4, 10009764, true}, // Upper limit + {10000000, 4, 10009765, false}, // Upper +1 + {10000000, 4, 9990236, true}, // Lower limit + {10000000, 4, 9990235, false}, // Lower limit -1 + // Banach to Banach {20000000, 5, 20000000, true}, {20000000, 5, 20019530, true}, // Upper limit {20000000, 5, 20019531, false}, // Upper limit +1 @@ -109,23 +109,18 @@ func TestBlockGasLimits(t *testing.T) { // TestCalcBaseFee assumes all blocks are 1559-blocks func TestCalcBaseFee(t *testing.T) { tests := []struct { - parentBaseFee int64 - parentGasLimit uint64 - parentGasUsed uint64 - expectedBaseFee int64 + parentL1BaseFee int64 + expectedL2BaseFee int64 }{ - {params.InitialBaseFee, 20000000, 10000000, params.InitialBaseFee}, // usage == target - {params.InitialBaseFee, 20000000, 9000000, 987500000}, // usage below target - {params.InitialBaseFee, 20000000, 11000000, 1012500000}, // usage above target + {0, 150000000}, + {1000000000, 164000000}, + {2000000000, 178000000}, + {100000000000, 1550000000}, + {111111111111, 1705555555}, + {1000000000000, 10000000000}, // cap at max L2 base fee } for i, test := range tests { - parent := &types.Header{ - Number: common.Big32, - GasLimit: test.parentGasLimit, - GasUsed: test.parentGasUsed, - BaseFee: big.NewInt(test.parentBaseFee), - } - if have, want := CalcBaseFee(config(), parent), big.NewInt(test.expectedBaseFee); have.Cmp(want) != 0 { + if have, want := CalcBaseFee(config(), nil, big.NewInt(test.parentL1BaseFee)), big.NewInt(test.expectedL2BaseFee); have.Cmp(want) != 0 { t.Errorf("test %d: have %d want %d, ", i, have, want) } } diff --git a/core/blockchain.go b/core/blockchain.go index ee9de85b82c8..add957a9440e 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -54,6 +54,8 @@ var ( headHeaderGauge = metrics.NewRegisteredGauge("chain/head/header", nil) headFastBlockGauge = metrics.NewRegisteredGauge("chain/head/receipt", nil) + l2BaseFeeGauge = metrics.NewRegisteredGauge("chain/fees/l2basefee", nil) + accountReadTimer = metrics.NewRegisteredTimer("chain/account/reads", nil) accountHashTimer = metrics.NewRegisteredTimer("chain/account/hashes", nil) accountUpdateTimer = metrics.NewRegisteredTimer("chain/account/updates", nil) @@ -1246,6 +1248,13 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types. return NonStatTy, errInsertionInterrupted } + // Note latest seen L2 base fee + if block.BaseFee() != nil { + l2BaseFeeGauge.Update(block.BaseFee().Int64()) + } else { + l2BaseFeeGauge.Update(0) + } + // Calculate the total difficulty of the block ptd := bc.GetTd(block.ParentHash(), block.NumberU64()-1) if ptd == nil { diff --git a/core/blockchain_test.go b/core/blockchain_test.go index 84bd9f08f338..365497c35935 100644 --- a/core/blockchain_test.go +++ b/core/blockchain_test.go @@ -607,7 +607,7 @@ func TestFastVsFullChains(t *testing.T) { gendb = rawdb.NewMemoryDatabase() key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") address = crypto.PubkeyToAddress(key.PublicKey) - funds = big.NewInt(1000000000000000) + funds = big.NewInt(3000000000000000) gspec = &Genesis{ Config: params.TestChainConfig, Alloc: GenesisAlloc{address: {Balance: funds}}, @@ -1704,6 +1704,7 @@ func TestInsertReceiptChainRollback(t *testing.T) { t.Fatalf("failed to create temp freezer db: %v", err) } gspec := Genesis{Config: params.AllEthashProtocolChanges} + gspec.BaseFee = big.NewInt(params.InitialBaseFee) gspec.MustCommit(ancientDb) ancientChain, _ := NewBlockChain(ancientDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil) defer ancientChain.Stop() @@ -3171,7 +3172,10 @@ func TestFeeVault(t *testing.T) { // Ensure that the fee vault received all tx fees actual = state.GetBalance(*params.TestChainConfig.Scroll.FeeVaultAddress) - expected = new(big.Int).SetUint64(block.GasUsed() * block.Transactions()[0].GasTipCap().Uint64()) + + effectiveGasPrice := new(big.Int).Add(block.BaseFee(), block.Transactions()[0].GasTipCap()) + gasUsed := new(big.Int).SetUint64(block.GasUsed()) + expected = new(big.Int).Mul(gasUsed, effectiveGasPrice) if actual.Cmp(expected) != 0 { t.Fatalf("fee vault balance incorrect: expected %d, got %d", expected, actual) diff --git a/core/chain_makers.go b/core/chain_makers.go index ff97716381d9..3920b2bef4d4 100644 --- a/core/chain_makers.go +++ b/core/chain_makers.go @@ -28,6 +28,7 @@ import ( "github.com/scroll-tech/go-ethereum/core/vm" "github.com/scroll-tech/go-ethereum/ethdb" "github.com/scroll-tech/go-ethereum/params" + "github.com/scroll-tech/go-ethereum/rollup/fees" ) // BlockGen creates blocks for testing. @@ -275,12 +276,9 @@ func makeHeader(chain consensus.ChainReader, parent *types.Block, state *state.S Number: new(big.Int).Add(parent.Number(), common.Big1), Time: time, } - if chain.Config().IsLondon(header.Number) { - header.BaseFee = misc.CalcBaseFee(chain.Config(), parent.Header()) - if !chain.Config().IsLondon(parent.Number()) { - parentGasLimit := parent.GasLimit() * params.ElasticityMultiplier - header.GasLimit = CalcGasLimit(parentGasLimit, parentGasLimit) - } + if chain.Config().IsBanach(header.Number) { + parentL1BaseFee := fees.GetL1BaseFee(state) + header.BaseFee = misc.CalcBaseFee(chain.Config(), parent.Header(), parentL1BaseFee) } return header } diff --git a/core/genesis.go b/core/genesis.go index 307468fd472e..50b77d4da2e8 100644 --- a/core/genesis.go +++ b/core/genesis.go @@ -28,6 +28,7 @@ import ( "github.com/scroll-tech/go-ethereum/common" "github.com/scroll-tech/go-ethereum/common/hexutil" "github.com/scroll-tech/go-ethereum/common/math" + "github.com/scroll-tech/go-ethereum/consensus/misc" "github.com/scroll-tech/go-ethereum/core/rawdb" "github.com/scroll-tech/go-ethereum/core/state" "github.com/scroll-tech/go-ethereum/core/types" @@ -312,13 +313,11 @@ func (g *Genesis) ToBlock(db ethdb.Database) *types.Block { if g.Difficulty == nil { head.Difficulty = params.GenesisDifficulty } - if g.Config != nil && g.Config.IsLondon(common.Big0) { + if g.Config != nil && g.Config.IsBanach(common.Big0) { if g.BaseFee != nil { head.BaseFee = g.BaseFee - } else if g.Config.Scroll.BaseFeeEnabled() { - head.BaseFee = new(big.Int).SetUint64(params.InitialBaseFee) } else { - head.BaseFee = nil + head.BaseFee = misc.CalcBaseFee(g.Config, nil, big.NewInt(0)) } } statedb.Commit(false) diff --git a/core/state_processor_test.go b/core/state_processor_test.go index 860c6da7410f..8c90264f27be 100644 --- a/core/state_processor_test.go +++ b/core/state_processor_test.go @@ -56,6 +56,7 @@ func TestStateProcessorErrors(t *testing.T) { BerlinBlock: big.NewInt(0), LondonBlock: big.NewInt(0), ShanghaiBlock: big.NewInt(0), + BanachBlock: big.NewInt(0), Ethash: new(params.EthashConfig), } signer = types.LatestSigner(config) @@ -332,6 +333,7 @@ func TestStateProcessorErrors(t *testing.T) { ArrowGlacierBlock: big.NewInt(0), ArchimedesBlock: big.NewInt(0), ShanghaiBlock: big.NewInt(0), + BanachBlock: big.NewInt(0), }, Alloc: GenesisAlloc{ common.HexToAddress("0x71562b71999873DB5b286dF957af199Ec94617F7"): GenesisAccount{ @@ -346,21 +348,22 @@ func TestStateProcessorErrors(t *testing.T) { smallInitCode = [320]byte{} ) defer blockchain.Stop() + parentL1BaseFee := big.NewInt(1000000000) // 1 gwei for i, tt := range []struct { txs []*types.Transaction want string }{ { // ErrMaxInitCodeSizeExceeded txs: []*types.Transaction{ - mkDynamicCreationTx(0, 500000, common.Big0, misc.CalcBaseFee(config, genesis.Header()), tooBigInitCode[:]), + mkDynamicCreationTx(0, 500000, common.Big0, misc.CalcBaseFee(config, genesis.Header(), parentL1BaseFee), tooBigInitCode[:]), }, - want: "could not apply tx 0 [0x7b33776d375660694a23ef992c090265682f3687607e0099b14503fdb65d73e3]: max initcode size exceeded: code size 49153 limit 49152", + want: "could not apply tx 0 [0xa3d2dadf08846f3328b84b0a38240ace42f79c99ea4b9c097c8dcb345f61a84e]: 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()), smallInitCode[:]), + mkDynamicCreationTx(0, 54299, common.Big0, misc.CalcBaseFee(config, genesis.Header(), parentL1BaseFee), smallInitCode[:]), }, - want: "could not apply tx 0 [0x98e54c5ecfa7986a66480d65ba32f2c6a2a6aedc3a67abb91b1e118b0717ed2d]: intrinsic gas too low: have 54299, want 54300", + want: "could not apply tx 0 [0x0880861c73bc504c01041b6d506737a119ca7c68c1fe666dc1682636b807c9db]: intrinsic gas too low: have 54299, want 54300", }, } { block := GenerateBadBlock(genesis, ethash.NewFaker(), tt.txs, gspec.Config) @@ -394,8 +397,10 @@ func GenerateBadBlock(parent *types.Block, engine consensus.Engine, txs types.Tr Time: parent.Time() + 10, UncleHash: types.EmptyUncleHash, } - if config.IsLondon(header.Number) { - header.BaseFee = misc.CalcBaseFee(config, parent.Header()) + + if config.IsBanach(header.Number) { + parentL1BaseFee := big.NewInt(1000000000) // 1 gwei + header.BaseFee = misc.CalcBaseFee(config, parent.Header(), parentL1BaseFee) } 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/state_transition.go b/core/state_transition.go index 7e7f0d8af592..fd1e07e4377f 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -288,6 +288,7 @@ func (st *StateTransition) preCheck() error { } } // Make sure that transaction gasFeeCap is greater than the baseFee (post london) + // Note: Logically, this should be `IsBanach`, but we keep `IsLondon` to ensure backward compatibility. if st.evm.ChainConfig().IsLondon(st.evm.Context.BlockNumber) { // Skip the checks if gas fields are zero and baseFee was explicitly disabled (eth_call) if !st.evm.Config.NoBaseFee || st.gasFeeCap.BitLen() > 0 || st.gasTipCap.BitLen() > 0 { @@ -411,12 +412,10 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) { st.refundGas(params.RefundQuotientEIP3529) } effectiveTip := st.gasPrice - if rules.IsLondon { - if st.evm.Context.BaseFee != nil { - effectiveTip = cmath.BigMin(st.gasTipCap, new(big.Int).Sub(st.gasFeeCap, st.evm.Context.BaseFee)) - } else { - effectiveTip = cmath.BigMin(st.gasTipCap, st.gasFeeCap) - } + + // only burn the base fee if the fee vault is not enabled + if rules.IsBanach && !st.evm.ChainConfig().Scroll.FeeVaultEnabled() { + effectiveTip = cmath.BigMin(st.gasTipCap, new(big.Int).Sub(st.gasFeeCap, st.evm.Context.BaseFee)) } // The L2 Fee is the same as the fee that is charged in the normal geth diff --git a/core/tx_pool.go b/core/tx_pool.go index b1aac9309225..e5e30fc6bf4a 100644 --- a/core/tx_pool.go +++ b/core/tx_pool.go @@ -1231,8 +1231,9 @@ func (pool *TxPool) runReorg(done chan struct{}, reset *txpoolResetRequest, dirt // because of another transaction (e.g. higher gas price). if reset != nil { pool.demoteUnexecutables() - if reset.newHead != nil && pool.chainconfig.IsLondon(new(big.Int).Add(reset.newHead.Number, big.NewInt(1))) { - pendingBaseFee := misc.CalcBaseFee(pool.chainconfig, reset.newHead) + if reset.newHead != nil && pool.chainconfig.IsBanach(new(big.Int).Add(reset.newHead.Number, big.NewInt(1))) { + l1BaseFee := fees.GetL1BaseFee(pool.currentState) + pendingBaseFee := misc.CalcBaseFee(pool.chainconfig, reset.newHead, l1BaseFee) pool.priced.SetBaseFee(pendingBaseFee) } // Update all accounts to the latest known pending nonce @@ -1356,8 +1357,8 @@ func (pool *TxPool) reset(oldHead, newHead *types.Header) { next := new(big.Int).Add(newHead.Number, big.NewInt(1)) pool.istanbul = pool.chainconfig.IsIstanbul(next) - pool.eip2718 = pool.chainconfig.Scroll.EnableEIP2718 && pool.chainconfig.IsBerlin(next) - pool.eip1559 = pool.chainconfig.Scroll.EnableEIP1559 && pool.chainconfig.IsLondon(next) + pool.eip2718 = pool.chainconfig.IsBanach(next) + pool.eip1559 = pool.chainconfig.IsBanach(next) pool.shanghai = pool.chainconfig.IsShanghai(next) } diff --git a/core/types/l2trace.go b/core/types/l2trace.go index 482afda64780..f106a548f61a 100644 --- a/core/types/l2trace.go +++ b/core/types/l2trace.go @@ -148,20 +148,23 @@ type StorageWrapper struct { } type TransactionData struct { - Type uint8 `json:"type"` - Nonce uint64 `json:"nonce"` - TxHash string `json:"txHash"` - Gas uint64 `json:"gas"` - GasPrice *hexutil.Big `json:"gasPrice"` - From common.Address `json:"from"` - To *common.Address `json:"to"` - ChainId *hexutil.Big `json:"chainId"` - Value *hexutil.Big `json:"value"` - Data string `json:"data"` - IsCreate bool `json:"isCreate"` - V *hexutil.Big `json:"v"` - R *hexutil.Big `json:"r"` - S *hexutil.Big `json:"s"` + Type uint8 `json:"type"` + Nonce uint64 `json:"nonce"` + TxHash string `json:"txHash"` + Gas uint64 `json:"gas"` + GasPrice *hexutil.Big `json:"gasPrice"` + GasTipCap *hexutil.Big `json:"gasTipCap"` + GasFeeCap *hexutil.Big `json:"gasFeeCap"` + From common.Address `json:"from"` + To *common.Address `json:"to"` + ChainId *hexutil.Big `json:"chainId"` + Value *hexutil.Big `json:"value"` + Data string `json:"data"` + IsCreate bool `json:"isCreate"` + AccessList AccessList `json:"accessList"` + V *hexutil.Big `json:"v"` + R *hexutil.Big `json:"r"` + S *hexutil.Big `json:"s"` } // NewTransactionData returns a transaction that will serialize to the trace @@ -177,20 +180,23 @@ func NewTransactionData(tx *Transaction, blockNumber uint64, config *params.Chai } result := &TransactionData{ - Type: tx.Type(), - TxHash: tx.Hash().String(), - Nonce: nonce, - ChainId: (*hexutil.Big)(tx.ChainId()), - From: from, - Gas: tx.Gas(), - GasPrice: (*hexutil.Big)(tx.GasPrice()), - To: tx.To(), - Value: (*hexutil.Big)(tx.Value()), - Data: hexutil.Encode(tx.Data()), - IsCreate: tx.To() == nil, - V: (*hexutil.Big)(v), - R: (*hexutil.Big)(r), - S: (*hexutil.Big)(s), + Type: tx.Type(), + TxHash: tx.Hash().String(), + Nonce: nonce, + ChainId: (*hexutil.Big)(tx.ChainId()), + From: from, + Gas: tx.Gas(), + GasPrice: (*hexutil.Big)(tx.GasPrice()), + GasTipCap: (*hexutil.Big)(tx.GasTipCap()), + GasFeeCap: (*hexutil.Big)(tx.GasFeeCap()), + To: tx.To(), + Value: (*hexutil.Big)(tx.Value()), + Data: hexutil.Encode(tx.Data()), + IsCreate: tx.To() == nil, + AccessList: tx.AccessList(), + V: (*hexutil.Big)(v), + R: (*hexutil.Big)(r), + S: (*hexutil.Big)(s), } return result } diff --git a/core/types/transaction_signing.go b/core/types/transaction_signing.go index 6a668aed9ef8..dea0afcc1a0c 100644 --- a/core/types/transaction_signing.go +++ b/core/types/transaction_signing.go @@ -40,6 +40,8 @@ type sigCache struct { func MakeSigner(config *params.ChainConfig, blockNumber *big.Int) Signer { var signer Signer switch { + case config.IsBanach(blockNumber): + signer = NewLondonSignerWithEIP4844(config.ChainID) case config.IsLondon(blockNumber): signer = NewLondonSignerWithEIP4844(config.ChainID) case config.IsBerlin(blockNumber): diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index 82e4396de1fe..9089d1342949 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -74,6 +74,8 @@ func NewEVMInterpreter(evm *EVM, cfg Config) *EVMInterpreter { if cfg.JumpTable[STOP] == nil { var jt JumpTable switch { + case evm.chainRules.IsBanach: + jt = banachInstructionSet case evm.chainRules.IsShanghai: jt = shanghaiInstructionSet case evm.chainRules.IsLondon: diff --git a/core/vm/jump_table.go b/core/vm/jump_table.go index 4112361b38cf..2116504dff1b 100644 --- a/core/vm/jump_table.go +++ b/core/vm/jump_table.go @@ -59,11 +59,20 @@ var ( berlinInstructionSet = newBerlinInstructionSet() londonInstructionSet = newLondonInstructionSet() shanghaiInstructionSet = newShanghaiInstructionSet() + banachInstructionSet = newBanachInstructionSet() ) // JumpTable contains the EVM opcodes supported at a given fork. type JumpTable [256]*operation +// newBanachInstructionSet returns the frontier, homestead, byzantium, +// contantinople, istanbul, petersburg, berlin, london, shanghai, and banach instructions. +func newBanachInstructionSet() JumpTable { + instructionSet := newShanghaiInstructionSet() + enable3198(&instructionSet) // Base fee opcode https://eips.ethereum.org/EIPS/eip-3198 + return instructionSet +} + // newShanghaiInstructionSet returns the frontier, homestead, byzantium, // contantinople, istanbul, petersburg, berlin, london and shanghai instructions. func newShanghaiInstructionSet() JumpTable { diff --git a/eth/api_backend.go b/eth/api_backend.go index b44510f6b943..fde05584cb16 100644 --- a/eth/api_backend.go +++ b/eth/api_backend.go @@ -380,3 +380,7 @@ func (b *EthAPIBackend) StateAtBlock(ctx context.Context, block *types.Block, re func (b *EthAPIBackend) StateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (core.Message, vm.BlockContext, *state.StateDB, error) { return b.eth.stateAtTransaction(block, txIndex, reexec) } + +func (b *EthAPIBackend) StateAt(root common.Hash) (*state.StateDB, error) { + return b.eth.BlockChain().StateAt(root) +} diff --git a/eth/catalyst/api.go b/eth/catalyst/api.go index 0b3cdecde06d..7a6c1a3ad9ec 100644 --- a/eth/catalyst/api.go +++ b/eth/catalyst/api.go @@ -32,6 +32,7 @@ import ( "github.com/scroll-tech/go-ethereum/log" "github.com/scroll-tech/go-ethereum/node" chainParams "github.com/scroll-tech/go-ethereum/params" + "github.com/scroll-tech/go-ethereum/rollup/fees" "github.com/scroll-tech/go-ethereum/rpc" "github.com/scroll-tech/go-ethereum/trie" ) @@ -141,8 +142,13 @@ func (api *consensusAPI) AssembleBlock(params assembleBlockParams) (*executableD Extra: []byte{}, Time: params.Timestamp, } - if config := api.eth.BlockChain().Config(); config.IsLondon(header.Number) { - header.BaseFee = misc.CalcBaseFee(config, parent.Header()) + if config := api.eth.BlockChain().Config(); config.IsBanach(header.Number) { + stateDb, err := api.eth.BlockChain().StateAt(parent.Root()) + if err != nil { + return nil, err + } + parentL1BaseFee := fees.GetL1BaseFee(stateDb) + header.BaseFee = misc.CalcBaseFee(config, parent.Header(), parentL1BaseFee) } err = api.eth.Engine().Prepare(bc, header) if err != nil { @@ -245,7 +251,7 @@ func decodeTransactions(enc [][]byte) ([]*types.Transaction, error) { return txs, nil } -func insertBlockParamsToBlock(config *chainParams.ChainConfig, parent *types.Header, params executableData) (*types.Block, error) { +func insertBlockParamsToBlock(config *chainParams.ChainConfig, parent *types.Header, params executableData, parentL1BaseFee *big.Int) (*types.Block, error) { txs, err := decodeTransactions(params.Transactions) if err != nil { return nil, err @@ -267,8 +273,8 @@ func insertBlockParamsToBlock(config *chainParams.ChainConfig, parent *types.Hea GasUsed: params.GasUsed, Time: params.Timestamp, } - if config.IsLondon(number) { - header.BaseFee = misc.CalcBaseFee(config, parent) + if config.IsBanach(number) { + header.BaseFee = misc.CalcBaseFee(config, parent, parentL1BaseFee) } block := types.NewBlockWithHeader(header).WithBody(txs, nil /* uncles */) return block, nil @@ -282,7 +288,12 @@ func (api *consensusAPI) NewBlock(params executableData) (*newBlockResponse, err if parent == nil { return &newBlockResponse{false}, fmt.Errorf("could not find parent %x", params.ParentHash) } - block, err := insertBlockParamsToBlock(api.eth.BlockChain().Config(), parent.Header(), params) + stateDb, err := api.eth.BlockChain().StateAt(parent.Root()) + if err != nil { + return nil, err + } + parentL1BaseFee := fees.GetL1BaseFee(stateDb) + block, err := insertBlockParamsToBlock(api.eth.BlockChain().Config(), parent.Header(), params, parentL1BaseFee) if err != nil { return nil, err } diff --git a/eth/gasprice/feehistory.go b/eth/gasprice/feehistory.go index 2e676ac7bc14..a8cf44138274 100644 --- a/eth/gasprice/feehistory.go +++ b/eth/gasprice/feehistory.go @@ -30,6 +30,7 @@ import ( "github.com/scroll-tech/go-ethereum/consensus/misc" "github.com/scroll-tech/go-ethereum/core/types" "github.com/scroll-tech/go-ethereum/log" + "github.com/scroll-tech/go-ethereum/rollup/fees" "github.com/scroll-tech/go-ethereum/rpc" ) @@ -88,8 +89,14 @@ func (oracle *Oracle) processBlock(bf *blockFees, percentiles []float64) { if bf.results.baseFee = bf.header.BaseFee; bf.results.baseFee == nil { bf.results.baseFee = new(big.Int) } - if chainconfig.IsLondon(big.NewInt(int64(bf.blockNumber + 1))) { - bf.results.nextBaseFee = misc.CalcBaseFee(chainconfig, bf.header) + if chainconfig.IsBanach(big.NewInt(int64(bf.blockNumber + 1))) { + state, err := oracle.backend.StateAt(bf.header.Root) + if err != nil || state == nil { + log.Error("State not found", "number", bf.header.Number, "hash", bf.header.Hash().Hex(), "state", state, "err", err) + return + } + l1BaseFee := fees.GetL1BaseFee(state) + bf.results.nextBaseFee = misc.CalcBaseFee(chainconfig, bf.header, l1BaseFee) } else { bf.results.nextBaseFee = new(big.Int) } @@ -191,10 +198,11 @@ func (oracle *Oracle) resolveBlockRange(ctx context.Context, lastBlock rpc.Block // actually processed range is returned to avoid ambiguity when parts of the requested range // are not available or when the head has changed during processing this request. // Three arrays are returned based on the processed blocks: -// - reward: the requested percentiles of effective priority fees per gas of transactions in each -// block, sorted in ascending order and weighted by gas used. -// - baseFee: base fee per gas in the given block -// - gasUsedRatio: gasUsed/gasLimit in the given block +// - reward: the requested percentiles of effective priority fees per gas of transactions in each +// block, sorted in ascending order and weighted by gas used. +// - baseFee: base fee per gas in the given block +// - gasUsedRatio: gasUsed/gasLimit in the given block +// // Note: baseFee includes the next block after the newest of the returned range, because this // value can be derived from the newest block. func (oracle *Oracle) FeeHistory(ctx context.Context, blocks int, unresolvedLastBlock rpc.BlockNumber, rewardPercentiles []float64) (*big.Int, [][]*big.Int, []*big.Int, []float64, error) { diff --git a/eth/gasprice/gasprice.go b/eth/gasprice/gasprice.go index 7c27fb424700..96ee73901d87 100644 --- a/eth/gasprice/gasprice.go +++ b/eth/gasprice/gasprice.go @@ -26,6 +26,7 @@ import ( "github.com/scroll-tech/go-ethereum/common" "github.com/scroll-tech/go-ethereum/core" + "github.com/scroll-tech/go-ethereum/core/state" "github.com/scroll-tech/go-ethereum/core/types" "github.com/scroll-tech/go-ethereum/event" "github.com/scroll-tech/go-ethereum/log" @@ -58,6 +59,7 @@ type OracleBackend interface { PendingBlockAndReceipts() (*types.Block, types.Receipts) ChainConfig() *params.ChainConfig SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription + StateAt(root common.Hash) (*state.StateDB, error) } // Oracle recommends gas prices based on the content of recent diff --git a/eth/gasprice/gasprice_test.go b/eth/gasprice/gasprice_test.go index 5cb58687f4ed..5fdaae2ca0ca 100644 --- a/eth/gasprice/gasprice_test.go +++ b/eth/gasprice/gasprice_test.go @@ -26,6 +26,7 @@ import ( "github.com/scroll-tech/go-ethereum/consensus/ethash" "github.com/scroll-tech/go-ethereum/core" "github.com/scroll-tech/go-ethereum/core/rawdb" + "github.com/scroll-tech/go-ethereum/core/state" "github.com/scroll-tech/go-ethereum/core/types" "github.com/scroll-tech/go-ethereum/core/vm" "github.com/scroll-tech/go-ethereum/crypto" @@ -108,6 +109,9 @@ func newTestBackend(t *testing.T, londonBlock *big.Int, pending bool) *testBacke ) config.LondonBlock = londonBlock config.ArrowGlacierBlock = londonBlock + config.ArchimedesBlock = londonBlock + config.ShanghaiBlock = londonBlock + config.BanachBlock = londonBlock engine := ethash.NewFaker() db := rawdb.NewMemoryDatabase() genesis, err := gspec.Commit(db) @@ -160,6 +164,10 @@ func (b *testBackend) GetBlockByNumber(number uint64) *types.Block { return b.chain.GetBlockByNumber(number) } +func (b *testBackend) StateAt(root common.Hash) (*state.StateDB, error) { + return nil, nil +} + func TestSuggestTipCap(t *testing.T) { config := Config{ Blocks: 3, diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index a9198a031494..fa476d1e148a 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -159,11 +159,20 @@ func (s *PublicTxPoolAPI) Content() map[string]map[string]map[string]*RPCTransac } pending, queue := s.b.TxPoolContent() curHeader := s.b.CurrentHeader() + + // get latest L1 base fee + state, err := s.b.StateAt(curHeader.Root) + if err != nil || state == nil { + log.Error("State not found", "number", curHeader.Number, "hash", curHeader.Hash().Hex(), "state", state, "err", err) + return nil + } + l1BaseFee := fees.GetL1BaseFee(state) + // Flatten the pending transactions for account, txs := range pending { dump := make(map[string]*RPCTransaction) for _, tx := range txs { - dump[fmt.Sprintf("%d", tx.Nonce())] = newRPCPendingTransaction(tx, curHeader, s.b.ChainConfig()) + dump[fmt.Sprintf("%d", tx.Nonce())] = newRPCPendingTransaction(tx, curHeader, s.b.ChainConfig(), l1BaseFee) } content["pending"][account.Hex()] = dump } @@ -171,7 +180,7 @@ func (s *PublicTxPoolAPI) Content() map[string]map[string]map[string]*RPCTransac for account, txs := range queue { dump := make(map[string]*RPCTransaction) for _, tx := range txs { - dump[fmt.Sprintf("%d", tx.Nonce())] = newRPCPendingTransaction(tx, curHeader, s.b.ChainConfig()) + dump[fmt.Sprintf("%d", tx.Nonce())] = newRPCPendingTransaction(tx, curHeader, s.b.ChainConfig(), l1BaseFee) } content["queued"][account.Hex()] = dump } @@ -184,17 +193,25 @@ func (s *PublicTxPoolAPI) ContentFrom(addr common.Address) map[string]map[string pending, queue := s.b.TxPoolContentFrom(addr) curHeader := s.b.CurrentHeader() + // get latest L1 base fee + state, err := s.b.StateAt(curHeader.Root) + if err != nil || state == nil { + log.Error("State not found", "number", curHeader.Number, "hash", curHeader.Hash().Hex(), "state", state, "err", err) + return nil + } + l1BaseFee := fees.GetL1BaseFee(state) + // Build the pending transactions dump := make(map[string]*RPCTransaction, len(pending)) for _, tx := range pending { - dump[fmt.Sprintf("%d", tx.Nonce())] = newRPCPendingTransaction(tx, curHeader, s.b.ChainConfig()) + dump[fmt.Sprintf("%d", tx.Nonce())] = newRPCPendingTransaction(tx, curHeader, s.b.ChainConfig(), l1BaseFee) } content["pending"] = dump // Build the queued transactions dump = make(map[string]*RPCTransaction, len(queue)) for _, tx := range queue { - dump[fmt.Sprintf("%d", tx.Nonce())] = newRPCPendingTransaction(tx, curHeader, s.b.ChainConfig()) + dump[fmt.Sprintf("%d", tx.Nonce())] = newRPCPendingTransaction(tx, curHeader, s.b.ChainConfig(), l1BaseFee) } content["queued"] = dump @@ -741,10 +758,10 @@ func (s *PublicBlockChainAPI) GetHeaderByHash(ctx context.Context, hash common.H } // GetBlockByNumber returns the requested canonical block. -// * When blockNr is -1 the chain head is returned. -// * When blockNr is -2 the pending chain head is returned. -// * When fullTx is true all transactions in the block are returned, otherwise -// only the transaction hash is returned. +// - When blockNr is -1 the chain head is returned. +// - When blockNr is -2 the pending chain head is returned. +// - When fullTx is true all transactions in the block are returned, otherwise +// only the transaction hash is returned. func (s *PublicBlockChainAPI) GetBlockByNumber(ctx context.Context, number rpc.BlockNumber, fullTx bool) (map[string]interface{}, error) { block, err := s.b.BlockByNumber(ctx, number) if block != nil && err == nil { @@ -1186,7 +1203,7 @@ func (s *PublicBlockChainAPI) EstimateGas(ctx context.Context, args TransactionA } // RPCMarshalHeader converts the given header to the RPC output . -func RPCMarshalHeader(head *types.Header, enableBaseFee bool) map[string]interface{} { +func RPCMarshalHeader(head *types.Header) map[string]interface{} { result := map[string]interface{}{ "number": (*hexutil.Big)(head.Number), "hash": head.Hash(), @@ -1207,7 +1224,7 @@ func RPCMarshalHeader(head *types.Header, enableBaseFee bool) map[string]interfa "receiptsRoot": head.ReceiptHash, } - if enableBaseFee && head.BaseFee != nil { + if head.BaseFee != nil { result["baseFeePerGas"] = (*hexutil.Big)(head.BaseFee) } @@ -1218,7 +1235,7 @@ func RPCMarshalHeader(head *types.Header, enableBaseFee bool) map[string]interfa // returned. When fullTx is true the returned block contains full transaction details, otherwise it will only contain // transaction hashes. func RPCMarshalBlock(block *types.Block, inclTx bool, fullTx bool, config *params.ChainConfig) (map[string]interface{}, error) { - fields := RPCMarshalHeader(block.Header(), config.Scroll.BaseFeeEnabled()) + fields := RPCMarshalHeader(block.Header()) fields["size"] = hexutil.Uint64(block.Size()) if inclTx { @@ -1253,7 +1270,7 @@ func RPCMarshalBlock(block *types.Block, inclTx bool, fullTx bool, config *param // rpcMarshalHeader uses the generalized output filler, then adds the total difficulty field, which requires // a `PublicBlockchainAPI`. func (s *PublicBlockChainAPI) rpcMarshalHeader(ctx context.Context, header *types.Header) map[string]interface{} { - fields := RPCMarshalHeader(header, s.b.ChainConfig().Scroll.BaseFeeEnabled()) + fields := RPCMarshalHeader(header) fields["totalDifficulty"] = (*hexutil.Big)(s.b.GetTd(ctx, header.Hash())) return fields } @@ -1351,11 +1368,11 @@ func NewRPCTransaction(tx *types.Transaction, blockHash common.Hash, blockNumber } // newRPCPendingTransaction returns a pending transaction that will serialize to the RPC representation -func newRPCPendingTransaction(tx *types.Transaction, current *types.Header, config *params.ChainConfig) *RPCTransaction { +func newRPCPendingTransaction(tx *types.Transaction, current *types.Header, config *params.ChainConfig, l1BaseFee *big.Int) *RPCTransaction { var baseFee *big.Int blockNumber := uint64(0) if current != nil { - baseFee = misc.CalcBaseFee(config, current) + baseFee = misc.CalcBaseFee(config, current, l1BaseFee) blockNumber = current.Number.Uint64() } return NewRPCTransaction(tx, common.Hash{}, blockNumber, 0, baseFee, config) @@ -1594,7 +1611,17 @@ func (s *PublicTransactionPoolAPI) GetTransactionByHash(ctx context.Context, has } // No finalized transaction, try to retrieve it from the pool if tx := s.b.GetPoolTransaction(hash); tx != nil { - return newRPCPendingTransaction(tx, s.b.CurrentHeader(), s.b.ChainConfig()), nil + curHeader := s.b.CurrentHeader() + + // get latest L1 base fee + state, err := s.b.StateAt(curHeader.Root) + if err != nil || state == nil { + log.Error("State not found", "number", curHeader.Number, "hash", curHeader.Hash().Hex(), "state", state, "err", err) + return nil, fmt.Errorf("cannot get L1 base fee, state not found") + } + l1BaseFee := fees.GetL1BaseFee(state) + + return newRPCPendingTransaction(tx, s.b.CurrentHeader(), s.b.ChainConfig(), l1BaseFee), nil } // Transaction unknown, return as such @@ -1654,7 +1681,7 @@ func (s *PublicTransactionPoolAPI) GetTransactionReceipt(ctx context.Context, ha "l1Fee": (*hexutil.Big)(receipt.L1Fee), } // Assign the effective gas price paid - if !s.b.ChainConfig().IsLondon(bigblock) { + if !s.b.ChainConfig().IsBanach(bigblock) { fields["effectiveGasPrice"] = hexutil.Uint64(tx.GasPrice().Uint64()) } else { header, err := s.b.HeaderByHash(ctx, blockHash) @@ -1866,10 +1893,19 @@ func (s *PublicTransactionPoolAPI) PendingTransactions() ([]*RPCTransaction, err } curHeader := s.b.CurrentHeader() transactions := make([]*RPCTransaction, 0, len(pending)) + + // get latest L1 base fee + state, err := s.b.StateAt(curHeader.Root) + if err != nil || state == nil { + log.Error("State not found", "number", curHeader.Number, "hash", curHeader.Hash().Hex(), "state", state, "err", err) + return nil, fmt.Errorf("cannot get L1 base fee, state not found") + } + l1BaseFee := fees.GetL1BaseFee(state) + for _, tx := range pending { from, _ := types.Sender(s.signer, tx) if _, exists := accounts[from]; exists { - transactions = append(transactions, newRPCPendingTransaction(tx, curHeader, s.b.ChainConfig())) + transactions = append(transactions, newRPCPendingTransaction(tx, curHeader, s.b.ChainConfig(), l1BaseFee)) } } return transactions, nil diff --git a/internal/ethapi/backend.go b/internal/ethapi/backend.go index c8764161f6e2..b2be3ae41a0a 100644 --- a/internal/ethapi/backend.go +++ b/internal/ethapi/backend.go @@ -65,6 +65,7 @@ type Backend interface { BlockByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*types.Block, error) StateAndHeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*state.StateDB, *types.Header, error) StateAndHeaderByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*state.StateDB, *types.Header, error) + StateAt(root common.Hash) (*state.StateDB, error) GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error) GetTd(ctx context.Context, hash common.Hash) *big.Int GetEVM(ctx context.Context, msg core.Message, state *state.StateDB, header *types.Header, vmConfig *vm.Config) (*vm.EVM, func() error, error) diff --git a/internal/ethapi/transaction_args.go b/internal/ethapi/transaction_args.go index ec1ad4cfce87..1b64f8b143a7 100644 --- a/internal/ethapi/transaction_args.go +++ b/internal/ethapi/transaction_args.go @@ -78,13 +78,13 @@ func (args *TransactionArgs) setDefaults(ctx context.Context, b Backend) error { if args.GasPrice != nil && (args.MaxFeePerGas != nil || args.MaxPriorityFeePerGas != nil) { return errors.New("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified") } - // After london, default to 1559 unless gasPrice is set + // After banach, default to 1559 unless gasPrice is set head := b.CurrentHeader() // If user specifies both maxPriorityfee and maxFee, then we do not - // need to consult the chain for defaults. It's definitely a London tx. + // need to consult the chain for defaults. It's definitely a Banach tx. if args.MaxPriorityFeePerGas == nil || args.MaxFeePerGas == nil { // In this clause, user left some fields unspecified. - if b.ChainConfig().IsLondon(head.Number) && args.GasPrice == nil { + if b.ChainConfig().IsBanach(head.Number) && args.GasPrice == nil { if args.MaxPriorityFeePerGas == nil { tip, err := b.SuggestGasTipCap(ctx) if err != nil { @@ -110,14 +110,14 @@ func (args *TransactionArgs) setDefaults(ctx context.Context, b Backend) error { } } else { if args.MaxFeePerGas != nil || args.MaxPriorityFeePerGas != nil { - return errors.New("maxFeePerGas or maxPriorityFeePerGas specified but london is not active yet") + return errors.New("maxFeePerGas or maxPriorityFeePerGas specified but banach is not active yet") } if args.GasPrice == nil { price, err := b.SuggestGasTipCap(ctx) if err != nil { return err } - if b.ChainConfig().IsLondon(head.Number) { + if b.ChainConfig().IsBanach(head.Number) { // The legacy tx gas price suggestion should not add 2x base fee // because all fees are consumed, so it would result in a spiral // upwards. diff --git a/les/api_backend.go b/les/api_backend.go index 2e2e1209f300..8c019cf3c3cc 100644 --- a/les/api_backend.go +++ b/les/api_backend.go @@ -19,6 +19,7 @@ package les import ( "context" "errors" + "fmt" "math/big" "time" @@ -336,3 +337,6 @@ func (b *LesApiBackend) StateAtBlock(ctx context.Context, block *types.Block, re func (b *LesApiBackend) StateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (core.Message, vm.BlockContext, *state.StateDB, error) { return b.eth.stateAtTransaction(ctx, block, txIndex, reexec) } +func (b *LesApiBackend) StateAt(root common.Hash) (*state.StateDB, error) { + return nil, fmt.Errorf("StateAt is not supported in LES protocol") +} diff --git a/miner/worker.go b/miner/worker.go index 744d3ef65776..99197551e1a9 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -39,6 +39,7 @@ import ( "github.com/scroll-tech/go-ethereum/metrics" "github.com/scroll-tech/go-ethereum/params" "github.com/scroll-tech/go-ethereum/rollup/circuitcapacitychecker" + "github.com/scroll-tech/go-ethereum/rollup/fees" "github.com/scroll-tech/go-ethereum/rollup/tracing" "github.com/scroll-tech/go-ethereum/trie" ) @@ -1322,20 +1323,15 @@ func (w *worker) commitNewWork(interrupt *int32, noempty bool, timestamp int64) Extra: w.extra, Time: uint64(timestamp), } - // Set baseFee and GasLimit if we are on an EIP-1559 chain - if w.chainConfig.IsLondon(header.Number) { - if w.chainConfig.Scroll.BaseFeeEnabled() { - header.BaseFee = misc.CalcBaseFee(w.chainConfig, parent.Header()) - } else { - // When disabling EIP-2718 or EIP-1559, we do not set baseFeePerGas in RPC response. - // Setting BaseFee as nil here can help outside SDK calculates l2geth's RLP encoding, - // otherwise the l2geth's BaseFee is not known from the outside. - header.BaseFee = nil - } - if !w.chainConfig.IsLondon(parent.Number()) { - parentGasLimit := parent.GasLimit() * params.ElasticityMultiplier - header.GasLimit = core.CalcGasLimit(parentGasLimit, w.config.GasCeil) + // Set baseFee if we are on an EIP-1559 chain + if w.chainConfig.IsBanach(header.Number) { + state, err := w.chain.StateAt(parent.Root()) + if err != nil { + log.Error("Failed to create mining context", "err", err) + return } + parentL1BaseFee := fees.GetL1BaseFee(state) + header.BaseFee = misc.CalcBaseFee(w.chainConfig, parent.Header(), parentL1BaseFee) } // 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 7d00e62808a9..75bb83a29dc2 100644 --- a/params/config.go +++ b/params/config.go @@ -289,8 +289,6 @@ var ( MaxTxPerBlock: &ScrollMaxTxPerBlock, MaxTxPayloadBytesPerBlock: &ScrollMaxTxPayloadBytesPerBlock, FeeVaultAddress: &rcfg.ScrollFeeVaultAddress, - EnableEIP2718: false, - EnableEIP1559: false, L1Config: &L1Config{ L1ChainId: 5, L1MessageQueueAddress: common.HexToAddress("0x79DB48002Aa861C8cb189cabc21c6B1468BC83BB"), @@ -328,8 +326,6 @@ var ( MaxTxPerBlock: &ScrollMaxTxPerBlock, MaxTxPayloadBytesPerBlock: &ScrollMaxTxPayloadBytesPerBlock, FeeVaultAddress: &rcfg.ScrollFeeVaultAddress, - EnableEIP2718: false, - EnableEIP1559: false, L1Config: &L1Config{ L1ChainId: 11155111, L1MessageQueueAddress: common.HexToAddress("0xF0B2293F5D834eAe920c6974D50957A1732de763"), @@ -367,8 +363,6 @@ var ( MaxTxPerBlock: &ScrollMaxTxPerBlock, MaxTxPayloadBytesPerBlock: &ScrollMaxTxPayloadBytesPerBlock, FeeVaultAddress: &rcfg.ScrollFeeVaultAddress, - EnableEIP2718: false, - EnableEIP1559: false, L1Config: &L1Config{ L1ChainId: 1, L1MessageQueueAddress: common.HexToAddress("0x0d7E906BD9cAFa154b048cFa766Cc1E54E39AF9B"), @@ -383,12 +377,10 @@ var ( // // This configuration is intentionally not using keyed fields to force anyone // adding flags to the config to also have to set these fields. - AllEthashProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, nil, new(EthashConfig), nil, + AllEthashProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, new(EthashConfig), nil, ScrollConfig{ UseZktrie: false, FeeVaultAddress: nil, - EnableEIP2718: true, - EnableEIP1559: true, MaxTxPerBlock: nil, MaxTxPayloadBytesPerBlock: nil, L1Config: &L1Config{5, common.HexToAddress("0x0000000000000000000000000000000000000000"), 0, common.HexToAddress("0x0000000000000000000000000000000000000000")}, @@ -399,35 +391,29 @@ var ( // // This configuration is intentionally not using keyed fields to force anyone // adding flags to the config to also have to set these fields. - AllCliqueProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, nil, nil, nil, &CliqueConfig{Period: 0, Epoch: 30000}, + AllCliqueProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, &CliqueConfig{Period: 0, Epoch: 30000}, ScrollConfig{ UseZktrie: false, FeeVaultAddress: nil, - EnableEIP2718: true, - EnableEIP1559: true, MaxTxPerBlock: nil, MaxTxPayloadBytesPerBlock: nil, L1Config: &L1Config{5, common.HexToAddress("0x0000000000000000000000000000000000000000"), 0, common.HexToAddress("0x0000000000000000000000000000000000000000")}, }} - TestChainConfig = &ChainConfig{big.NewInt(1), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, nil, new(EthashConfig), nil, + TestChainConfig = &ChainConfig{big.NewInt(1), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, new(EthashConfig), nil, ScrollConfig{ UseZktrie: false, FeeVaultAddress: &common.Address{123}, - EnableEIP2718: true, - EnableEIP1559: true, MaxTxPerBlock: nil, MaxTxPayloadBytesPerBlock: nil, L1Config: &L1Config{5, common.HexToAddress("0x0000000000000000000000000000000000000000"), 0, common.HexToAddress("0x0000000000000000000000000000000000000000")}, }} TestRules = TestChainConfig.Rules(new(big.Int)) - TestNoL1DataFeeChainConfig = &ChainConfig{big.NewInt(1), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, nil, new(EthashConfig), nil, + TestNoL1DataFeeChainConfig = &ChainConfig{big.NewInt(1), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, new(EthashConfig), nil, ScrollConfig{ UseZktrie: false, FeeVaultAddress: nil, - EnableEIP2718: true, - EnableEIP1559: true, MaxTxPerBlock: nil, MaxTxPayloadBytesPerBlock: nil, L1Config: &L1Config{5, common.HexToAddress("0x0000000000000000000000000000000000000000"), 0, common.HexToAddress("0x0000000000000000000000000000000000000000")}, @@ -539,12 +525,6 @@ type ScrollConfig struct { // Transaction fee vault address [optional] FeeVaultAddress *common.Address `json:"feeVaultAddress,omitempty"` - // Enable EIP-2718 in tx pool [optional] - EnableEIP2718 bool `json:"enableEIP2718,omitempty"` - - // Enable EIP-1559 in tx pool, EnableEIP2718 should be true too [optional] - EnableEIP1559 bool `json:"enableEIP1559,omitempty"` - // L1 config L1Config *L1Config `json:"l1Config,omitempty"` } @@ -566,10 +546,6 @@ func (c *L1Config) String() string { c.L1ChainId, c.L1MessageQueueAddress.Hex(), c.NumL1MessagesPerBlock, c.ScrollChainAddress.Hex()) } -func (s ScrollConfig) BaseFeeEnabled() bool { - return s.EnableEIP2718 && s.EnableEIP1559 -} - func (s ScrollConfig) FeeVaultEnabled() bool { return s.FeeVaultAddress != nil } @@ -593,8 +569,8 @@ func (s ScrollConfig) String() string { maxTxPayloadBytesPerBlock = fmt.Sprintf("%v", *s.MaxTxPayloadBytesPerBlock) } - return fmt.Sprintf("{useZktrie: %v, maxTxPerBlock: %v, MaxTxPayloadBytesPerBlock: %v, feeVaultAddress: %v, enableEIP2718: %v, enableEIP1559: %v, l1Config: %v}", - s.UseZktrie, maxTxPerBlock, maxTxPayloadBytesPerBlock, s.FeeVaultAddress, s.EnableEIP2718, s.EnableEIP1559, s.L1Config.String()) + return fmt.Sprintf("{useZktrie: %v, maxTxPerBlock: %v, MaxTxPayloadBytesPerBlock: %v, feeVaultAddress: %v, l1Config: %v}", + s.UseZktrie, maxTxPerBlock, maxTxPayloadBytesPerBlock, s.FeeVaultAddress, s.L1Config.String()) } // IsValidTxCount returns whether the given block's transaction count is below the limit. diff --git a/params/version.go b/params/version.go index 0bf067e2222d..597b5d4d43f9 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 = 1 // Minor version component of the current release - VersionPatch = 21 // Patch version component of the current release + VersionPatch = 22 // Patch version component of the current release VersionMeta = "mainnet" // Version metadata to append to the version string ) diff --git a/rollup/fees/rollup_fee.go b/rollup/fees/rollup_fee.go index 29110a1e9b56..312471a1af6a 100644 --- a/rollup/fees/rollup_fee.go +++ b/rollup/fees/rollup_fee.go @@ -187,3 +187,7 @@ func CalculateL1DataFee(tx *types.Transaction, state StateDB) (*big.Int, error) l1DataFee := calculateEncodedL1DataFee(raw, overhead, l1BaseFee, scalar) return l1DataFee, nil } + +func GetL1BaseFee(state StateDB) *big.Int { + return state.GetState(rcfg.L1GasPriceOracleAddress, rcfg.L1BaseFeeSlot).Big() +} diff --git a/tests/state_test_util.go b/tests/state_test_util.go index 0172239e71c2..e259f9e62ba2 100644 --- a/tests/state_test_util.go +++ b/tests/state_test_util.go @@ -188,7 +188,7 @@ func (t *StateTest) RunNoVerify(subtest StateSubtest, vmconfig vm.Config, snapsh snaps, statedb := MakePreState(rawdb.NewMemoryDatabase(), t.json.Pre, snapshotter) var baseFee *big.Int - if config.IsLondon(new(big.Int)) { + if config.IsBanach(new(big.Int)) { baseFee = t.json.Env.BaseFee if baseFee == nil { // Retesteth uses `0x10` for genesis baseFee. Therefore, it defaults to