mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-24 21:56:43 +00:00
feat(feynman): L2 fee calculation (#1199)
* Update L2 base fee calculation to account for Feynman * Update consensus/misc/eip1559.go Co-authored-by: Péter Garamvölgyi <peter@scroll.io> * Use dedicated system parameter provingBaseFee * Merge and change initialBaseFee * Check on parent not to fail tests * fix test * fix golint * fix goimport * fix unit tests * check config.IsFeynman(parent.Time) * Update consensus/misc/eip1559.go * fix fee transition * remove an incorrect comment * reuse baseFeeOverhead as proving base fee * update da-codec commit * address comments * add a comment * add base fee max check * add upstream 1559 tests * bump version --------- Co-authored-by: Péter Garamvölgyi <peter@scroll.io> Co-authored-by: colin <102356659+colinlyguo@users.noreply.github.com> Co-authored-by: colinlyguo <colinlyguo@scroll.io>
This commit is contained in:
parent
27e6e9e168
commit
c604c90159
13 changed files with 134 additions and 20 deletions
|
|
@ -102,14 +102,88 @@ 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()
|
||||
|
||||
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.
|
||||
func MinBaseFee() *big.Int {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
)
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue