Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable London EIPs #1422

Merged
merged 8 commits into from
May 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
50 changes: 3 additions & 47 deletions consensus/misc/eip1559.go
Expand Up @@ -20,8 +20,6 @@ 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"
)
Expand All @@ -30,18 +28,12 @@ import (
// - 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 {
Expand All @@ -53,41 +45,5 @@ func VerifyEip1559Header(config *params.ChainConfig, parent, header *types.Heade

// 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,
)
}
return new(big.Int).SetUint64(params.InitialBaseFee)
}
4 changes: 2 additions & 2 deletions consensus/misc/eip1559_test.go
Expand Up @@ -115,8 +115,8 @@ func TestCalcBaseFee(t *testing.T) {
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
{params.InitialBaseFee, 20000000, 9000000, params.InitialBaseFee}, // usage below target
{params.InitialBaseFee, 20000000, 11000000, params.InitialBaseFee}, // usage above target
}
for i, test := range tests {
parent := &types.Header{
Expand Down
57 changes: 39 additions & 18 deletions consensus/parlia/parlia.go
Expand Up @@ -394,6 +394,22 @@ func getVoteAttestationFromHeader(header *types.Header, chainConfig *params.Chai
return &attestation, nil
}

// getParent returns the parent of a given block.
func (p *Parlia) getParent(chain consensus.ChainHeaderReader, header *types.Header, parents []*types.Header) (*types.Header, error) {
Mister-EA marked this conversation as resolved.
Show resolved Hide resolved
var parent *types.Header
number := header.Number.Uint64()
if len(parents) > 0 {
parent = parents[len(parents)-1]
} else {
parent = chain.GetHeader(header.ParentHash, number-1)
}

if parent == nil || parent.Number.Uint64() != number-1 || parent.Hash() != header.ParentHash {
return nil, consensus.ErrUnknownAncestor
}
return parent, nil
}

// verifyVoteAttestation checks whether the vote attestation in the header is valid.
func (p *Parlia) verifyVoteAttestation(chain consensus.ChainHeaderReader, header *types.Header, parents []*types.Header) error {
attestation, err := getVoteAttestationFromHeader(header, p.chainConfig, p.config)
Expand All @@ -411,15 +427,9 @@ func (p *Parlia) verifyVoteAttestation(chain consensus.ChainHeaderReader, header
}

// Get parent block
number := header.Number.Uint64()
var parent *types.Header
if len(parents) > 0 {
parent = parents[len(parents)-1]
} else {
parent = chain.GetHeader(header.ParentHash, number-1)
}
if parent == nil || parent.Hash() != header.ParentHash {
return consensus.ErrUnknownAncestor
parent, err := p.getParent(chain, header, parents)
if err != nil {
return err
}

// The target block should be direct parent.
Expand Down Expand Up @@ -541,6 +551,23 @@ func (p *Parlia) verifyHeader(chain consensus.ChainHeaderReader, header *types.H
if err := misc.VerifyForkHashes(chain.Config(), header, false); err != nil {
return err
}

parent, err := p.getParent(chain, header, parents)
if err != nil {
return err
}

// Verify the block's gas usage and (if applicable) verify the base fee.
if !chain.Config().IsLondon(header.Number) {
Mister-EA marked this conversation as resolved.
Show resolved Hide resolved
// 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)
}
} else if err := misc.VerifyEip1559Header(chain.Config(), parent, header); err != nil {
// Verify the header's EIP-1559 attributes.
return err
}

// All basic checks passed, verify cascading fields
return p.verifyCascadingFields(chain, header, parents)
}
Expand All @@ -556,15 +583,9 @@ func (p *Parlia) verifyCascadingFields(chain consensus.ChainHeaderReader, header
return nil
}

var parent *types.Header
if len(parents) > 0 {
parent = parents[len(parents)-1]
} else {
parent = chain.GetHeader(header.ParentHash, number-1)
}

if parent == nil || parent.Number.Uint64() != number-1 || parent.Hash() != header.ParentHash {
return consensus.ErrUnknownAncestor
parent, err := p.getParent(chain, header, parents)
if err != nil {
return err
}

snap, err := p.snapshot(chain, number-1, header.ParentHash, parents)
Expand Down
165 changes: 165 additions & 0 deletions core/blockchain_eip3529_test.go
@@ -0,0 +1,165 @@
package core

import (
"math/big"
"testing"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/consensus"
"github.com/ethereum/go-ethereum/consensus/ethash"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/params"
)

func postLondonConfig() *params.ChainConfig {
config := *params.TestChainConfig
config.LondonBlock = big.NewInt(0)
return &config
}

func preLondonConfig() *params.ChainConfig {
config := *params.TestChainConfig
config.LondonBlock = nil
return &config
Mister-EA marked this conversation as resolved.
Show resolved Hide resolved
}

func TestSelfDestructGasPreEIP3529(t *testing.T) {
bytecode := []byte{
byte(vm.PC),
byte(vm.SELFDESTRUCT),
}

// Expected gas is (intrinsic + pc + cold load (due to legacy tx) + selfdestruct cost ) / 2
// The refund of 24000 gas (i.e. params.SelfdestructRefundGas) is not applied since refunds pre-EIP3529 are
// capped to half of the transaction's gas.
expectedGasUsed := (params.TxGas + vm.GasQuickStep + params.ColdAccountAccessCostEIP2929 + params.SelfdestructGasEIP150) / 2
testGasUsage(t, preLondonConfig(), ethash.NewFaker(), bytecode, nil, 60_000, expectedGasUsed)
brilliant-lx marked this conversation as resolved.
Show resolved Hide resolved
}

func TestSstoreGasPreEIP3529(t *testing.T) {
bytecode := []byte{
byte(vm.PUSH1), 0x3, // value
byte(vm.PUSH1), 0x3, // location
byte(vm.SSTORE), // Set slot[3] = 3
}
// // Expected gas is intrinsic + 2*pushGas + cold load (due to legacy tx) + SstoreGas
expectedGasUsed := params.TxGas + 2*vm.GasFastestStep + params.ColdSloadCostEIP2929 + params.SstoreSetGasEIP2200
brilliant-lx marked this conversation as resolved.
Show resolved Hide resolved
testGasUsage(t, preLondonConfig(), ethash.NewFaker(), bytecode, nil, 60_000, expectedGasUsed)
}

func TestSelfDestructGasPostEIP3529(t *testing.T) {
bytecode := []byte{
byte(vm.PC),
byte(vm.SELFDESTRUCT),
}
// Expected gas is intrinsic + pc + cold load (due to legacy tx) + SelfDestructGas
expectedGasUsed := params.TxGas + vm.GasQuickStep + params.ColdAccountAccessCostEIP2929 + params.SelfdestructGasEIP150
testGasUsage(t, postLondonConfig(), ethash.NewFaker(), bytecode, nil, 60_000, expectedGasUsed)
}

func TestSstoreGasPostEIP3529(t *testing.T) {
bytecode := []byte{
byte(vm.PUSH1), 0x3, // value
byte(vm.PUSH1), 0x3, // location
byte(vm.SSTORE), // Set slot[3] = 3
}
// Expected gas is intrinsic + 2*pushGas + cold load (due to legacy tx) + SstoreGas
expectedGasUsed := params.TxGas + 2*vm.GasFastestStep + params.ColdSloadCostEIP2929 + params.SstoreSetGasEIP2200
testGasUsage(t, postLondonConfig(), ethash.NewFaker(), bytecode, nil, 60_000, expectedGasUsed)
}

func TestSstoreModifyGasPostEIP3529(t *testing.T) {
bytecode := []byte{
byte(vm.PUSH1), 0x3, // value
byte(vm.PUSH1), 0x1, // location
byte(vm.SSTORE), // Set slot[1] = 3
}
// initialize contract storage
initialStorage := make(map[common.Hash]common.Hash)
// Populate two slots
initialStorage[common.HexToHash("01")] = common.HexToHash("01")
initialStorage[common.HexToHash("02")] = common.HexToHash("02")
// Expected gas is intrinsic + 2*pushGas + cold load (due to legacy tx) + SstoreReset (a->b such that a!=0)
expectedGasUsed := params.TxGas + 2*vm.GasFastestStep + params.ColdSloadCostEIP2929 + (params.SstoreResetGasEIP2200 - params.ColdSloadCostEIP2929)
testGasUsage(t, postLondonConfig(), ethash.NewFaker(), bytecode, initialStorage, 60_000, expectedGasUsed)
}

func TestSstoreClearGasPostEIP3529(t *testing.T) {
bytecode := []byte{
byte(vm.PUSH1), 0x0, // value
byte(vm.PUSH1), 0x1, // location
byte(vm.SSTORE), // Set slot[1] = 0
}
// initialize contract storage
initialStorage := make(map[common.Hash]common.Hash)
// Populate two slots
initialStorage[common.HexToHash("01")] = common.HexToHash("01")
initialStorage[common.HexToHash("02")] = common.HexToHash("02")

// Expected gas is intrinsic + 2*pushGas + cold load (due to legacy tx) + SstoreReset (a->b such that a!=0) - sstoreClearGasRefund
expectedGasUsage := params.TxGas + 2*vm.GasFastestStep + params.ColdSloadCostEIP2929 + (params.SstoreResetGasEIP2200 - params.ColdSloadCostEIP2929) - params.SstoreClearsScheduleRefundEIP3529
testGasUsage(t, postLondonConfig(), ethash.NewFaker(), bytecode, initialStorage, 60_000, expectedGasUsage)
}

// Test the gas used by running a transaction sent to a smart contract with given bytecode and storage.
func testGasUsage(t *testing.T, config *params.ChainConfig, engine consensus.Engine, bytecode []byte, initialStorage map[common.Hash]common.Hash, initialGas, expectedGasUsed uint64) {
var (
aa = common.HexToAddress("0x000000000000000000000000000000000000aaaa")

// Generate a canonical chain to act as the main dataset
db = rawdb.NewMemoryDatabase()

// A sender who makes transactions, has some funds
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
address = crypto.PubkeyToAddress(key.PublicKey)
balanceBefore = big.NewInt(1000000000000000)
gspec = &Genesis{
Config: config,
Alloc: GenesisAlloc{
address: {Balance: balanceBefore},
aa: {
Code: bytecode,
Storage: initialStorage,
Nonce: 0,
Balance: big.NewInt(0),
},
},
}
genesis = gspec.MustCommit(db)
)

blocks, _ := GenerateChain(gspec.Config, genesis, engine, db, 1, func(i int, b *BlockGen) {
b.SetCoinbase(common.Address{1})

// One transaction to 0xAAAA
signer := types.LatestSigner(gspec.Config)
tx, _ := types.SignNewTx(key, signer, &types.LegacyTx{
Nonce: 0,
To: &aa,
Gas: initialGas,
GasPrice: newGwei(5),
})
b.AddTx(tx)
})

// Import the canonical chain
diskdb := rawdb.NewMemoryDatabase()
gspec.MustCommit(diskdb)

chain, err := NewBlockChain(diskdb, nil, gspec.Config, engine, vm.Config{}, nil, nil)
if err != nil {
t.Fatalf("failed to create tester chain: %v", err)
}
if n, err := chain.InsertChain(blocks); err != nil {
t.Fatalf("block %d: failed to insert into chain: %v", n, err)
}

block := chain.GetBlockByNumber(1)

if block.GasUsed() != expectedGasUsed {
t.Fatalf("incorrect amount of gas spent: expected %d, got %d", expectedGasUsed, block.GasUsed())
}
}
4 changes: 2 additions & 2 deletions core/state_processor_test.go
Expand Up @@ -160,11 +160,11 @@ func TestStateProcessorErrors(t *testing.T) {
},
want: "could not apply tx 0 [0xbd49d8dadfd47fb846986695f7d4da3f7b2c48c8da82dbc211a26eb124883de9]: gas limit reached",
},
{ // ErrFeeCapTooLow
{ // ErrFeeCapTooLow does not apply because default BaseFee is 0
txs: []*types.Transaction{
mkDynamicTx(0, common.Address{}, params.TxGas, big.NewInt(0), big.NewInt(0)),
},
want: "could not apply tx 0 [0xc4ab868fef0c82ae0387b742aee87907f2d0fc528fc6ea0a021459fb0fc4a4a8]: max fee per gas less than block base fee: address 0x71562b71999873DB5b286dF957af199Ec94617F7, maxFeePerGas: 0 baseFee: 875000000",
want: "invalid gas used (remote: 0 local: 21000)",
},
{ // ErrTipVeryHigh
txs: []*types.Transaction{
Expand Down
11 changes: 6 additions & 5 deletions core/state_transition.go
Expand Up @@ -353,14 +353,15 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) {
st.refundGas(params.RefundQuotientEIP3529)
}

effectiveTip := st.gasPrice
if rules.IsLondon {
effectiveTip = cmath.BigMin(st.gasTipCap, new(big.Int).Sub(st.gasFeeCap, st.evm.Context.BaseFee))
Mister-EA marked this conversation as resolved.
Show resolved Hide resolved
}

// consensus engine is parlia
if st.evm.ChainConfig().Parlia != nil {
st.state.AddBalance(consensus.SystemAddress, new(big.Int).Mul(new(big.Int).SetUint64(st.gasUsed()), st.gasPrice))
st.state.AddBalance(consensus.SystemAddress, new(big.Int).Mul(new(big.Int).SetUint64(st.gasUsed()), effectiveTip))
} else {
effectiveTip := st.gasPrice
if rules.IsLondon {
effectiveTip = cmath.BigMin(st.gasTipCap, new(big.Int).Sub(st.gasFeeCap, st.evm.Context.BaseFee))
}
st.state.AddBalance(st.evm.Context.Coinbase, new(big.Int).Mul(new(big.Int).SetUint64(st.gasUsed()), effectiveTip))
}

Expand Down
5 changes: 3 additions & 2 deletions core/types/block_test.go
Expand Up @@ -69,7 +69,7 @@ func TestBlockEncoding(t *testing.T) {
}

func TestEIP1559BlockEncoding(t *testing.T) {
blockEnc := common.FromHex("f9030bf901fea083cafc574e1f51ba9dc0568fc617a08ea2429fb384059c972f13b19fa1c8dd55a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a0ef1552a40b7165c3cd773806b9e0c165b75356e0314bf0706f279c729f51e017a05fe50b260da6308036625b850b5d6ced6d0a9f814c0688bc91ffb7b7a3a54b67a0bc37d79753ad738a6dac4921e57392f145d8887476de3f783dfa7edae9283e52b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302000001832fefd8825208845506eb0780a0bd4472abb6659ebe3ee06ee4d7b72a00a9f4d001caca51342001075469aff49888a13a5a8c8f2bb1c4843b9aca00f90106f85f800a82c35094095e7baea6a6c7c4c2dfeb977efac326af552d870a801ba09bea4c4daac7c7c52e093e6a4c35dbbcf8856f1af7b059ba20253e70848d094fa08a8fae537ce25ed8cb5af9adac3f141af69bd515bd2ba031522df09b97dd72b1b8a302f8a0018080843b9aca008301e24194095e7baea6a6c7c4c2dfeb977efac326af552d878080f838f7940000000000000000000000000000000000000001e1a0000000000000000000000000000000000000000000000000000000000000000080a0fe38ca4e44a30002ac54af7cf922a6ac2ba11b7d22f548e8ecb3f51f41cb31b0a06de6a5cbae13c0c856e33acf021b51819636cfc009d39eafb9f606d546e305a8c0")
blockEnc := common.FromHex("f90303f901faa083cafc574e1f51ba9dc0568fc617a08ea2429fb384059c972f13b19fa1c8dd55a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a0ef1552a40b7165c3cd773806b9e0c165b75356e0314bf0706f279c729f51e017a05fe50b260da6308036625b850b5d6ced6d0a9f814c0688bc91ffb7b7a3a54b67a0bc37d79753ad738a6dac4921e57392f145d8887476de3f783dfa7edae9283e52b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302000001832fefd8825208845506eb0780a0bd4472abb6659ebe3ee06ee4d7b72a00a9f4d001caca51342001075469aff49888a13a5a8c8f2bb1c480f90102f85f800a82c35094095e7baea6a6c7c4c2dfeb977efac326af552d870a801ba09bea4c4daac7c7c52e093e6a4c35dbbcf8856f1af7b059ba20253e70848d094fa08a8fae537ce25ed8cb5af9adac3f141af69bd515bd2ba031522df09b97dd72b1b89f02f89c018080808301e24194095e7baea6a6c7c4c2dfeb977efac326af552d878080f838f7940000000000000000000000000000000000000001e1a0000000000000000000000000000000000000000000000000000000000000000080a0fe38ca4e44a30002ac54af7cf922a6ac2ba11b7d22f548e8ecb3f51f41cb31b0a06de6a5cbae13c0c856e33acf021b51819636cfc009d39eafb9f606d546e305a8c0")
var block Block
if err := rlp.DecodeBytes(blockEnc, &block); err != nil {
t.Fatal("decode error: ", err)
Expand All @@ -87,7 +87,7 @@ func TestEIP1559BlockEncoding(t *testing.T) {
check("Coinbase", block.Coinbase(), common.HexToAddress("8888f1f195afa192cfee860698584c030f4c9db1"))
check("MixDigest", block.MixDigest(), common.HexToHash("bd4472abb6659ebe3ee06ee4d7b72a00a9f4d001caca51342001075469aff498"))
check("Root", block.Root(), common.HexToHash("ef1552a40b7165c3cd773806b9e0c165b75356e0314bf0706f279c729f51e017"))
check("Hash", block.Hash(), common.HexToHash("c7252048cd273fe0dac09650027d07f0e3da4ee0675ebbb26627cea92729c372"))
check("Hash", block.Hash(), common.HexToHash("0xae9d971d16de73f69f940f1bd4ce5961158781176e73abea92a2b8781403885e"))
check("Nonce", block.Nonce(), uint64(0xa13a5a8c8f2bb1c4))
check("Time", block.Time(), uint64(1426516743))
check("Size", block.Size(), common.StorageSize(len(blockEnc)))
Expand Down Expand Up @@ -122,6 +122,7 @@ func TestEIP1559BlockEncoding(t *testing.T) {

check("len(Transactions)", len(block.Transactions()), 2)
check("Transactions[0].Hash", block.Transactions()[0].Hash(), tx1.Hash())
check("Transactions[1].inner", block.Transactions()[1].inner, tx2.inner)
check("Transactions[1].Hash", block.Transactions()[1].Hash(), tx2.Hash())
check("Transactions[1].Type", block.Transactions()[1].Type(), tx2.Type())
ourBlockEnc, err := rlp.EncodeToBytes(&block)
Expand Down