Skip to content

Commit

Permalink
feat: re-enable EIP-1559 in Banach hard fork (#634)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
Thegaram committed Mar 11, 2024
1 parent dab4354 commit ccec84c
Show file tree
Hide file tree
Showing 32 changed files with 272 additions and 229 deletions.
2 changes: 1 addition & 1 deletion accounts/abi/bind/backends/simulated.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion accounts/abi/bind/base.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
9 changes: 5 additions & 4 deletions cmd/evm/internal/t8ntool/transition.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"))
}
}
Expand Down Expand Up @@ -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.
Expand Down
2 changes: 1 addition & 1 deletion consensus/clique/clique.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 <nil>", header.BaseFee)
Expand Down
2 changes: 1 addition & 1 deletion consensus/ethash/consensus.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
89 changes: 26 additions & 63 deletions consensus/misc/eip1559.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
41 changes: 18 additions & 23 deletions consensus/misc/eip1559_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)
Expand Down Expand Up @@ -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
}

Expand All @@ -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
Expand Down Expand Up @@ -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)
}
}
Expand Down
9 changes: 9 additions & 0 deletions core/blockchain.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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 {
Expand Down
8 changes: 6 additions & 2 deletions core/blockchain_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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}},
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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)
Expand Down
10 changes: 4 additions & 6 deletions core/chain_makers.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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
}
Expand Down
7 changes: 3 additions & 4 deletions core/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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)
Expand Down
Loading

0 comments on commit ccec84c

Please sign in to comment.