core, consensus/misc, params: implement EIP-7997 (#35223)

EIP: https://eips.ethereum.org/EIPS/eip-7997
This commit is contained in:
rjl493456442 2026-07-02 20:56:18 +08:00 committed by GitHub
parent 46b79ea078
commit c8953d10c2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 189 additions and 0 deletions

47
consensus/misc/eip7997.go Normal file
View file

@ -0,0 +1,47 @@
// Copyright 2026 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 (
"github.com/ethereum/go-ethereum/core/tracing"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/params"
)
// ApplyEIP7997 inserts the deterministic deployment factory into the state as an
// irregular state transition, as specified by EIP-7997. The factory is a keyless
// CREATE2 factory that, once present at the canonical address on every EVM chain,
// allows contracts to be deployed at identical addresses across chains.
func ApplyEIP7997(statedb vm.StateDB) {
// The account must hold the canonical factory runtime code. If its code hash
// already matches, the chain satisfies EIP-7997 and nothing needs to change.
wantHash := crypto.Keccak256Hash(params.DeterministicFactoryCode)
if statedb.GetCodeHash(params.DeterministicFactoryAddress) == wantHash {
return
}
if !statedb.Exist(params.DeterministicFactoryAddress) {
statedb.CreateAccount(params.DeterministicFactoryAddress)
}
statedb.CreateContract(params.DeterministicFactoryAddress)
statedb.SetCode(params.DeterministicFactoryAddress, params.DeterministicFactoryCode, tracing.CodeChangeUnspecified)
// Preserve a pre-existing nonce; only bump the default zero nonce to 1.
if statedb.GetNonce(params.DeterministicFactoryAddress) == 0 {
statedb.SetNonce(params.DeterministicFactoryAddress, 1, tracing.NonceChangeNewContract)
}
}

View file

@ -385,6 +385,11 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse
if config.DAOForkSupport && config.DAOForkBlock != nil && config.DAOForkBlock.Cmp(b.header.Number) == 0 {
misc.ApplyDAOHardFork(statedb)
}
// EIP-7997: insert the deterministic deployment factory at the Amsterdam
// activation block via an irregular state transition.
if config.IsAmsterdam(b.header.Number, b.header.Time) && !config.IsAmsterdam(parent.Number(), parent.Time()) {
misc.ApplyEIP7997(statedb)
}
if config.IsPrague(b.header.Number, b.header.Time) || config.IsUBT(b.header.Number, b.header.Time) {
// EIP-2935

113
core/eip7997_test.go Normal file
View file

@ -0,0 +1,113 @@
// Copyright 2026 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/>.
// Tests for EIP-7997: the deterministic deployment factory inserted as an
// irregular state transition at the Amsterdam activation block.
package core
import (
"bytes"
"testing"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/consensus/misc"
"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"
"github.com/holiman/uint256"
)
// TestApplyEIP7997 verifies the irregular state transition seeds the factory
// account with the canonical code and nonce.
func TestApplyEIP7997(t *testing.T) {
sdb := mkState(nil)
misc.ApplyEIP7997(sdb)
if got := sdb.GetCode(params.DeterministicFactoryAddress); !bytes.Equal(got, params.DeterministicFactoryCode) {
t.Fatalf("factory code mismatch:\n got %x\nwant %x", got, params.DeterministicFactoryCode)
}
if got := sdb.GetNonce(params.DeterministicFactoryAddress); got != 1 {
t.Fatalf("factory nonce = %d, want %d", got, 1)
}
}
// TestApplyEIP7997Existing checks that a chain which already hosts the factory
// (for example via its keyless creation transaction) is left untouched, so the
// transition never rewrites an existing nonce.
func TestApplyEIP7997Existing(t *testing.T) {
sdb := mkState(types.GenesisAlloc{
params.DeterministicFactoryAddress: {Code: params.DeterministicFactoryCode, Nonce: 5},
})
misc.ApplyEIP7997(sdb)
if got := sdb.GetNonce(params.DeterministicFactoryAddress); got != 5 {
t.Fatalf("existing factory nonce overwritten: got %d, want 5", got)
}
}
// TestApplyEIP7997WrongCode checks that an account occupying the factory address
// with the wrong code is force-overwritten with the canonical runtime code, while
// a pre-existing non-zero nonce is preserved.
func TestApplyEIP7997WrongCode(t *testing.T) {
sdb := mkState(types.GenesisAlloc{
params.DeterministicFactoryAddress: {Code: []byte{0x60, 0x00}, Nonce: 7},
})
misc.ApplyEIP7997(sdb)
if got := sdb.GetCode(params.DeterministicFactoryAddress); !bytes.Equal(got, params.DeterministicFactoryCode) {
t.Fatalf("factory code not overwritten:\n got %x\nwant %x", got, params.DeterministicFactoryCode)
}
if got := sdb.GetNonce(params.DeterministicFactoryAddress); got != 7 {
t.Fatalf("factory nonce = %d, want %d (existing nonce must be preserved)", got, 7)
}
}
// TestEIP7997FactoryDeploys exercises the inserted factory bytecode: calling it
// with a salt followed by init code must CREATE2-deploy the contract at the
// canonical deterministic address and return that address (20 bytes, unpadded).
func TestEIP7997FactoryDeploys(t *testing.T) {
sdb := mkState(nil)
misc.ApplyEIP7997(sdb)
var (
caller = common.Address{0xca}
salt [32]byte
// initcode returning the single-byte runtime 0xfe:
// PUSH1 0xfe PUSH1 0x00 MSTORE8 PUSH1 0x01 PUSH1 0x00 RETURN
initcode = common.FromHex("60fe60005360016000f3")
)
salt[31] = 0x42
input := append(append([]byte{}, salt[:]...), initcode...)
ret, _, err := amsterdamCoreEVM(sdb).Call(caller, params.DeterministicFactoryAddress, input, vm.NewGasBudget(10_000_000, 0), new(uint256.Int))
if err != nil {
t.Fatalf("factory call failed: %v", err)
}
want := crypto.CreateAddress2(params.DeterministicFactoryAddress, salt, crypto.Keccak256(initcode))
if len(ret) != 20 {
t.Fatalf("factory returned %d bytes, want 20", len(ret))
}
if got := common.BytesToAddress(ret); got != want {
t.Fatalf("factory returned address %x, want %x", got, want)
}
if code := sdb.GetCode(want); !bytes.Equal(code, []byte{0xfe}) {
t.Fatalf("deployed runtime code = %x, want fe", code)
}
}

View file

@ -725,6 +725,8 @@ func DeveloperGenesisBlock(gasLimit uint64, faucet *common.Address) *Genesis {
// EIP-8282 - Builder Execution Requests
params.BuilderDepositAddress: {Nonce: 1, Code: params.BuilderDepositCode, Balance: common.Big0},
params.BuilderExitAddress: {Nonce: 1, Code: params.BuilderExitCode, Balance: common.Big0},
// EIP-7997 - Deterministic deployment factory
params.DeterministicFactoryAddress: {Nonce: 1, Code: params.DeterministicFactoryCode, Balance: common.Big0},
},
}
if faucet != nil {

View file

@ -81,6 +81,11 @@ func (p *StateProcessor) Process(ctx context.Context, block *types.Block, stated
if config.DAOForkSupport && config.DAOForkBlock != nil && config.DAOForkBlock.Cmp(block.Number()) == 0 {
misc.ApplyDAOHardFork(tracingStateDB)
}
// EIP-7997: insert the deterministic deployment factory at the Amsterdam
// activation block via an irregular state transition.
if isEIP7997Transition(config, p.chain, header) {
misc.ApplyEIP7997(tracingStateDB)
}
var (
context = NewEVMBlockContext(header, p.chain, nil)
signer = types.MakeSigner(config, header.Number, header.Time)
@ -137,6 +142,19 @@ func (p *StateProcessor) Process(ctx context.Context, block *types.Block, stated
}, nil
}
// isEIP7997Transition reports whether the given header belongs to the first block
// on which the Amsterdam fork is active.
func isEIP7997Transition(config *params.ChainConfig, chain ChainContext, header *types.Header) bool {
if header.Number.Sign() == 0 || !config.IsAmsterdam(header.Number, header.Time) {
return false
}
parent := chain.GetHeader(header.ParentHash, header.Number.Uint64()-1)
if parent == nil {
return false
}
return !config.IsAmsterdam(parent.Number, parent.Time)
}
// PreExecution processes pre-execution system calls.
func PreExecution(ctx context.Context, beaconRoot *common.Hash, parent common.Hash, config *params.ChainConfig, evm *vm.EVM, number *big.Int, time uint64) *bal.ConstructionBlockAccessList {
_, _, spanEnd := telemetry.StartSpan(ctx, "core.preExecution")

View file

@ -270,6 +270,10 @@ var (
BuilderDepositCode = common.FromHex("0x3373fffffffffffffffffffffffffffffffffffffffe146101065760115f54807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1461023457600182026001905f5b5f82111560695781019083028483029004916001019190604e565b90939004925050503660b814608957366102345734610234575f5260205ff35b8034106102345760383567ffffffffffffffff1680633b9aca001161023457633b9aca00029034031061023457600154600101600155600354806006026004015f358155600101602035815560010160403581556001016060358155600101608035815560010160a035905560b85f5f3760b85fa0600101600355005b600354600254808203806101001161011d57506101005b5f5b8181146101c3578281016006026004018160b8028154815260200181600101548152602001816002015480825260401c67ffffffffffffffff16816010018160381c81600701538160301c81600601538160281c81600501538160201c81600401538160181c81600301538160101c81600201538160081c81600101535360200181600301548152602001816004015481526020019060050154905260010161011f565b91018092146101d557906002556101e0565b90505f6002555f6003555b5f54807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff141561020d57505f5b6001546020828201116102225750505f610228565b01602090035b5f555f60015560b8025ff35b5f5ffd")
BuilderExitAddress = common.HexToAddress("0x000014574A74c805590AFF9499fc7A690f008282")
BuilderExitCode = common.FromHex("0x3373fffffffffffffffffffffffffffffffffffffffe1460cb5760115f54807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1461018857600182026001905f5b5f82111560685781019083028483029004916001019190604d565b909390049250505036603014608857366101885734610188575f5260205ff35b341061018857600154600101600155600354806003026004013381556001015f35815560010160203590553360601b5f5260305f60143760445fa0600101600355005b6003546002548082038060101160df575060105b5f5b8181146101175782810160030260040181604402815460601b8152601401816001015481526020019060020154905260010160e1565b91018092146101295790600255610134565b90505f6002555f6003555b5f54807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff141561016157505f5b6001546002828201116101765750505f61017c565b01600290035b5f555f6001556044025ff35b5f5ffd")
// EIP-7997 - Deterministic deployment factory (keyless CREATE2 factory)
DeterministicFactoryAddress = common.HexToAddress("0x4e59b44847b379578588920cA78FbF26c0B4956C")
DeterministicFactoryCode = common.FromHex("0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe03601600081602082378035828234f58015156039578182fd5b8082525050506014600cf3")
)
// System log events.