all: implement EIP-1559 (#22837)
This is the initial implementation of EIP-1559 in packages core/types and core. Mining, RPC, etc. will be added in subsequent commits. Co-authored-by: Marius van der Wijden <m.vanderwijden@live.de> Co-authored-by: lightclient@protonmail.com <lightclient@protonmail.com> Co-authored-by: Felix Lange <fjl@twurst.com>
This commit is contained in:
committed by
GitHub
parent
14bc6e5130
commit
94451c2788
@ -299,10 +299,6 @@ func (c *Clique) verifyHeader(chain consensus.ChainHeaderReader, header *types.H
|
||||
if header.GasLimit > cap {
|
||||
return fmt.Errorf("invalid gasLimit: have %v, max %v", header.GasLimit, cap)
|
||||
}
|
||||
// Verify that the gasUsed is <= gasLimit
|
||||
if header.GasUsed > header.GasLimit {
|
||||
return fmt.Errorf("invalid gasUsed: have %d, gasLimit %d", header.GasUsed, header.GasLimit)
|
||||
}
|
||||
// If all checks passed, validate any special fields for hard forks
|
||||
if err := misc.VerifyForkHashes(chain.Config(), header, false); err != nil {
|
||||
return err
|
||||
@ -334,14 +330,21 @@ func (c *Clique) verifyCascadingFields(chain consensus.ChainHeaderReader, header
|
||||
if parent.Time+c.config.Period > header.Time {
|
||||
return errInvalidTimestamp
|
||||
}
|
||||
// Verify that the gas limit remains within allowed bounds
|
||||
diff := int64(parent.GasLimit) - int64(header.GasLimit)
|
||||
if diff < 0 {
|
||||
diff *= -1
|
||||
// Verify that the gasUsed is <= gasLimit
|
||||
if header.GasUsed > header.GasLimit {
|
||||
return fmt.Errorf("invalid gasUsed: have %d, gasLimit %d", header.GasUsed, header.GasLimit)
|
||||
}
|
||||
limit := parent.GasLimit / params.GasLimitBoundDivisor
|
||||
if uint64(diff) >= limit || header.GasLimit < params.MinGasLimit {
|
||||
return fmt.Errorf("invalid gas limit: have %d, want %d += %d", header.GasLimit, parent.GasLimit, limit)
|
||||
if !chain.Config().IsLondon(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)
|
||||
}
|
||||
if err := misc.VerifyGaslimit(parent.GasLimit, header.GasLimit); err != nil {
|
||||
return err
|
||||
}
|
||||
} else if err := misc.VerifyEip1559Header(chain.Config(), parent, header); err != nil {
|
||||
// Verify the header's EIP-1559 attributes.
|
||||
return err
|
||||
}
|
||||
// Retrieve the snapshot needed to verify this header and cache it
|
||||
snap, err := c.snapshot(chain, number-1, header.ParentHash, parents)
|
||||
@ -725,7 +728,7 @@ func CliqueRLP(header *types.Header) []byte {
|
||||
}
|
||||
|
||||
func encodeSigHeader(w io.Writer, header *types.Header) {
|
||||
err := rlp.Encode(w, []interface{}{
|
||||
enc := []interface{}{
|
||||
header.ParentHash,
|
||||
header.UncleHash,
|
||||
header.Coinbase,
|
||||
@ -741,8 +744,11 @@ func encodeSigHeader(w io.Writer, header *types.Header) {
|
||||
header.Extra[:len(header.Extra)-crypto.SignatureLength], // Yes, this will panic if extra is too short
|
||||
header.MixDigest,
|
||||
header.Nonce,
|
||||
})
|
||||
if err != nil {
|
||||
}
|
||||
if header.BaseFee != nil {
|
||||
enc = append(enc, header.BaseFee)
|
||||
}
|
||||
if err := rlp.Encode(w, enc); err != nil {
|
||||
panic("can't encode: " + err.Error())
|
||||
}
|
||||
}
|
||||
|
@ -284,16 +284,18 @@ func (ethash *Ethash) verifyHeader(chain consensus.ChainHeaderReader, header, pa
|
||||
if header.GasUsed > header.GasLimit {
|
||||
return fmt.Errorf("invalid gasUsed: have %d, gasLimit %d", header.GasUsed, header.GasLimit)
|
||||
}
|
||||
|
||||
// Verify that the gas limit remains within allowed bounds
|
||||
diff := int64(parent.GasLimit) - int64(header.GasLimit)
|
||||
if diff < 0 {
|
||||
diff *= -1
|
||||
}
|
||||
limit := parent.GasLimit / params.GasLimitBoundDivisor
|
||||
|
||||
if uint64(diff) >= limit || header.GasLimit < params.MinGasLimit {
|
||||
return fmt.Errorf("invalid gas limit: have %d, want %d += %d", header.GasLimit, parent.GasLimit, limit)
|
||||
// Verify the block's gas usage and (if applicable) verify the base fee.
|
||||
if !chain.Config().IsLondon(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)
|
||||
}
|
||||
if err := misc.VerifyGaslimit(parent.GasLimit, header.GasLimit); err != nil {
|
||||
return err
|
||||
}
|
||||
} else if err := misc.VerifyEip1559Header(chain.Config(), parent, header); err != nil {
|
||||
// Verify the header's EIP-1559 attributes.
|
||||
return err
|
||||
}
|
||||
// Verify that the block number is parent's +1
|
||||
if diff := new(big.Int).Sub(header.Number, parent.Number); diff.Cmp(big.NewInt(1)) != 0 {
|
||||
@ -604,7 +606,7 @@ func (ethash *Ethash) FinalizeAndAssemble(chain consensus.ChainHeaderReader, hea
|
||||
func (ethash *Ethash) SealHash(header *types.Header) (hash common.Hash) {
|
||||
hasher := sha3.NewLegacyKeccak256()
|
||||
|
||||
rlp.Encode(hasher, []interface{}{
|
||||
enc := []interface{}{
|
||||
header.ParentHash,
|
||||
header.UncleHash,
|
||||
header.Coinbase,
|
||||
@ -618,7 +620,11 @@ func (ethash *Ethash) SealHash(header *types.Header) (hash common.Hash) {
|
||||
header.GasUsed,
|
||||
header.Time,
|
||||
header.Extra,
|
||||
})
|
||||
}
|
||||
if header.BaseFee != nil {
|
||||
enc = append(enc, header.BaseFee)
|
||||
}
|
||||
rlp.Encode(hasher, enc)
|
||||
hasher.Sum(hash[:0])
|
||||
return hash
|
||||
}
|
||||
|
93
consensus/misc/eip1559.go
Normal file
93
consensus/misc/eip1559.go
Normal file
@ -0,0 +1,93 @@
|
||||
// Copyright 2021 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package misc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/math"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
|
||||
// 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 {
|
||||
return err
|
||||
}
|
||||
// Verify the header is not malformed
|
||||
if header.BaseFee == nil {
|
||||
return fmt.Errorf("header is missing baseFee")
|
||||
}
|
||||
// Verify the baseFee is correct based on the parent header.
|
||||
expectedBaseFee := CalcBaseFee(config, parent)
|
||||
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)
|
||||
}
|
||||
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)
|
||||
}
|
||||
|
||||
var (
|
||||
parentGasTarget = parent.GasLimit / params.ElasticityMultiplier
|
||||
parentGasTargetBig = new(big.Int).SetUint64(parentGasTarget)
|
||||
baseFeeChangeDenominator = new(big.Int).SetUint64(params.BaseFeeChangeDenominator)
|
||||
)
|
||||
// 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,
|
||||
)
|
||||
|
||||
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)
|
||||
|
||||
return math.BigMax(
|
||||
x.Sub(parent.BaseFee, baseFeeDelta),
|
||||
common.Big0,
|
||||
)
|
||||
}
|
||||
}
|
133
consensus/misc/eip1559_test.go
Normal file
133
consensus/misc/eip1559_test.go
Normal file
@ -0,0 +1,133 @@
|
||||
// Copyright 2021 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package misc
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
|
||||
// copyConfig does a _shallow_ copy of a given config. Safe to set new values, but
|
||||
// do not use e.g. SetInt() on the numbers. For testing only
|
||||
func copyConfig(original *params.ChainConfig) *params.ChainConfig {
|
||||
return ¶ms.ChainConfig{
|
||||
ChainID: original.ChainID,
|
||||
HomesteadBlock: original.HomesteadBlock,
|
||||
DAOForkBlock: original.DAOForkBlock,
|
||||
DAOForkSupport: original.DAOForkSupport,
|
||||
EIP150Block: original.EIP150Block,
|
||||
EIP150Hash: original.EIP150Hash,
|
||||
EIP155Block: original.EIP155Block,
|
||||
EIP158Block: original.EIP158Block,
|
||||
ByzantiumBlock: original.ByzantiumBlock,
|
||||
ConstantinopleBlock: original.ConstantinopleBlock,
|
||||
PetersburgBlock: original.PetersburgBlock,
|
||||
IstanbulBlock: original.IstanbulBlock,
|
||||
MuirGlacierBlock: original.MuirGlacierBlock,
|
||||
BerlinBlock: original.BerlinBlock,
|
||||
LondonBlock: original.LondonBlock,
|
||||
EWASMBlock: original.EWASMBlock,
|
||||
CatalystBlock: original.CatalystBlock,
|
||||
Ethash: original.Ethash,
|
||||
Clique: original.Clique,
|
||||
}
|
||||
}
|
||||
|
||||
func config() *params.ChainConfig {
|
||||
config := copyConfig(params.TestChainConfig)
|
||||
config.LondonBlock = big.NewInt(5)
|
||||
return config
|
||||
}
|
||||
|
||||
// TestBlockGasLimits tests the gasLimit checks for blocks both across
|
||||
// the EIP-1559 boundary and post-1559 blocks
|
||||
func TestBlockGasLimits(t *testing.T) {
|
||||
initial := new(big.Int).SetUint64(params.InitialBaseFee)
|
||||
|
||||
for i, tc := range []struct {
|
||||
pGasLimit uint64
|
||||
pNum int64
|
||||
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
|
||||
{20000000, 5, 20000000, true},
|
||||
{20000000, 5, 20019530, true}, // Upper limit
|
||||
{20000000, 5, 20019531, false}, // Upper limit +1
|
||||
{20000000, 5, 19980470, true}, // Lower limit
|
||||
{20000000, 5, 19980469, false}, // Lower limit -1
|
||||
{40000000, 5, 40039061, true}, // Upper limit
|
||||
{40000000, 5, 40039062, false}, // Upper limit +1
|
||||
{40000000, 5, 39960939, true}, // lower limit
|
||||
{40000000, 5, 39960938, false}, // Lower limit -1
|
||||
} {
|
||||
parent := &types.Header{
|
||||
GasUsed: tc.pGasLimit / 2,
|
||||
GasLimit: tc.pGasLimit,
|
||||
BaseFee: initial,
|
||||
Number: big.NewInt(tc.pNum),
|
||||
}
|
||||
header := &types.Header{
|
||||
GasUsed: tc.gasLimit / 2,
|
||||
GasLimit: tc.gasLimit,
|
||||
BaseFee: initial,
|
||||
Number: big.NewInt(tc.pNum + 1),
|
||||
}
|
||||
err := VerifyEip1559Header(config(), parent, header)
|
||||
if tc.ok && err != nil {
|
||||
t.Errorf("test %d: Expected valid header: %s", i, err)
|
||||
}
|
||||
if !tc.ok && err == nil {
|
||||
t.Errorf("test %d: Expected invalid header", i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestCalcBaseFee assumes all blocks are 1559-blocks
|
||||
func TestCalcBaseFee(t *testing.T) {
|
||||
tests := []struct {
|
||||
parentBaseFee int64
|
||||
parentGasLimit uint64
|
||||
parentGasUsed uint64
|
||||
expectedBaseFee 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
|
||||
}
|
||||
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 {
|
||||
t.Errorf("test %d: have %d want %d, ", i, have, want)
|
||||
}
|
||||
}
|
||||
}
|
42
consensus/misc/gaslimit.go
Normal file
42
consensus/misc/gaslimit.go
Normal file
@ -0,0 +1,42 @@
|
||||
// Copyright 2021 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package misc
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
|
||||
// VerifyGaslimit verifies the header gas limit according increase/decrease
|
||||
// in relation to the parent gas limit.
|
||||
func VerifyGaslimit(parentGasLimit, headerGasLimit uint64) error {
|
||||
// Verify that the gas limit remains within allowed bounds
|
||||
diff := int64(parentGasLimit) - int64(headerGasLimit)
|
||||
if diff < 0 {
|
||||
diff *= -1
|
||||
}
|
||||
limit := parentGasLimit / params.GasLimitBoundDivisor
|
||||
if uint64(diff) >= limit {
|
||||
return fmt.Errorf("invalid gas limit: have %d, want %d +-= %d", headerGasLimit, parentGasLimit, limit-1)
|
||||
}
|
||||
if headerGasLimit < params.MinGasLimit {
|
||||
return errors.New("invalid gas limit below 5000")
|
||||
}
|
||||
return nil
|
||||
}
|
Reference in New Issue
Block a user