Commit bbf2c5b7 authored by Joshua Gutow's avatar Joshua Gutow Committed by GitHub

Merge pull request #8129 from mdehoog/create2deployer-genesis

Ensure genesis contains create2deployer if canyon is enabled at timestamp 0
parents 1aeb3f20 44057030
......@@ -25,6 +25,7 @@ const (
L1FeeVault = "0x420000000000000000000000000000000000001a"
SchemaRegistry = "0x4200000000000000000000000000000000000020"
EAS = "0x4200000000000000000000000000000000000021"
Create2Deployer = "0x13b0D85CcB8bf860b6b79AF3029fCA081AE9beF2"
)
var (
......@@ -47,39 +48,48 @@ var (
L1FeeVaultAddr = common.HexToAddress(L1FeeVault)
SchemaRegistryAddr = common.HexToAddress(SchemaRegistry)
EASAddr = common.HexToAddress(EAS)
Create2DeployerAddr = common.HexToAddress(Create2Deployer)
Predeploys = make(map[string]*common.Address)
Predeploys = make(map[string]*Predeploy)
PredeploysByAddress = make(map[common.Address]*Predeploy)
)
// IsProxied returns true for predeploys that will sit behind a proxy contract
func IsProxied(predeployAddr common.Address) bool {
switch predeployAddr {
case WETH9Addr:
case GovernanceTokenAddr:
default:
return true
func init() {
Predeploys["L2ToL1MessagePasser"] = &Predeploy{Address: L2ToL1MessagePasserAddr}
Predeploys["DeployerWhitelist"] = &Predeploy{Address: DeployerWhitelistAddr}
Predeploys["WETH9"] = &Predeploy{Address: WETH9Addr, ProxyDisabled: true}
Predeploys["L2CrossDomainMessenger"] = &Predeploy{Address: L2CrossDomainMessengerAddr}
Predeploys["L2StandardBridge"] = &Predeploy{Address: L2StandardBridgeAddr}
Predeploys["SequencerFeeVault"] = &Predeploy{Address: SequencerFeeVaultAddr}
Predeploys["OptimismMintableERC20Factory"] = &Predeploy{Address: OptimismMintableERC20FactoryAddr}
Predeploys["L1BlockNumber"] = &Predeploy{Address: L1BlockNumberAddr}
Predeploys["GasPriceOracle"] = &Predeploy{Address: GasPriceOracleAddr}
Predeploys["L1Block"] = &Predeploy{Address: L1BlockAddr}
Predeploys["GovernanceToken"] = &Predeploy{
Address: GovernanceTokenAddr,
ProxyDisabled: true,
Enabled: func(config DeployConfig) bool {
return config.GovernanceEnabled()
},
}
Predeploys["LegacyMessagePasser"] = &Predeploy{Address: LegacyMessagePasserAddr}
Predeploys["L2ERC721Bridge"] = &Predeploy{Address: L2ERC721BridgeAddr}
Predeploys["OptimismMintableERC721Factory"] = &Predeploy{Address: OptimismMintableERC721FactoryAddr}
Predeploys["ProxyAdmin"] = &Predeploy{Address: ProxyAdminAddr}
Predeploys["BaseFeeVault"] = &Predeploy{Address: BaseFeeVaultAddr}
Predeploys["L1FeeVault"] = &Predeploy{Address: L1FeeVaultAddr}
Predeploys["SchemaRegistry"] = &Predeploy{Address: SchemaRegistryAddr}
Predeploys["EAS"] = &Predeploy{Address: EASAddr}
Predeploys["Create2Deployer"] = &Predeploy{
Address: Create2DeployerAddr,
ProxyDisabled: true,
Enabled: func(config DeployConfig) bool {
canyonTime := config.CanyonTime(0)
return canyonTime != nil && *canyonTime == 0
},
}
return false
}
func init() {
Predeploys["L2ToL1MessagePasser"] = &L2ToL1MessagePasserAddr
Predeploys["DeployerWhitelist"] = &DeployerWhitelistAddr
Predeploys["WETH9"] = &WETH9Addr
Predeploys["L2CrossDomainMessenger"] = &L2CrossDomainMessengerAddr
Predeploys["L2StandardBridge"] = &L2StandardBridgeAddr
Predeploys["SequencerFeeVault"] = &SequencerFeeVaultAddr
Predeploys["OptimismMintableERC20Factory"] = &OptimismMintableERC20FactoryAddr
Predeploys["L1BlockNumber"] = &L1BlockNumberAddr
Predeploys["GasPriceOracle"] = &GasPriceOracleAddr
Predeploys["L1Block"] = &L1BlockAddr
Predeploys["GovernanceToken"] = &GovernanceTokenAddr
Predeploys["LegacyMessagePasser"] = &LegacyMessagePasserAddr
Predeploys["L2ERC721Bridge"] = &L2ERC721BridgeAddr
Predeploys["OptimismMintableERC721Factory"] = &OptimismMintableERC721FactoryAddr
Predeploys["ProxyAdmin"] = &ProxyAdminAddr
Predeploys["BaseFeeVault"] = &BaseFeeVaultAddr
Predeploys["L1FeeVault"] = &L1FeeVaultAddr
Predeploys["SchemaRegistry"] = &SchemaRegistryAddr
Predeploys["EAS"] = &EASAddr
for _, predeploy := range Predeploys {
PredeploysByAddress[predeploy.Address] = predeploy
}
}
package predeploys
import (
"github.com/ethereum/go-ethereum/common"
)
type DeployConfig interface {
GovernanceEnabled() bool
CanyonTime(genesisTime uint64) *uint64
}
type Predeploy struct {
Address common.Address
ProxyDisabled bool
Enabled func(config DeployConfig) bool
}
......@@ -99,8 +99,8 @@ func entrypoint(ctx *cli.Context) error {
log.Info("All predeploy proxies are set correctly")
// Check that all of the defined predeploys are set up correctly
for name, addr := range predeploys.Predeploys {
log.Info("Checking predeploy", "name", name, "address", addr.Hex())
for name, pre := range predeploys.Predeploys {
log.Info("Checking predeploy", "name", name, "address", pre.Address.Hex())
if err := checkPredeployConfig(clients.L2Client, name); err != nil {
return err
}
......@@ -112,7 +112,7 @@ func entrypoint(ctx *cli.Context) error {
func checkPredeploy(client *ethclient.Client, i uint64) error {
bigAddr := new(big.Int).Or(genesis.BigL2PredeployNamespace, new(big.Int).SetUint64(i))
addr := common.BigToAddress(bigAddr)
if !predeploys.IsProxied(addr) {
if pre, ok := predeploys.PredeploysByAddress[addr]; ok && pre.ProxyDisabled {
return nil
}
admin, err := getEIP1967AdminAddress(client, addr)
......@@ -131,10 +131,10 @@ func checkPredeployConfig(client *ethclient.Client, name string) error {
if predeploy == nil {
return fmt.Errorf("unknown predeploy %s", name)
}
p := *predeploy
p := predeploy.Address
g := new(errgroup.Group)
if predeploys.IsProxied(p) {
if !predeploy.ProxyDisabled {
// Check that an implementation is set. If the implementation has been upgraded,
// it will be considered non-standard. Ensure that there is code set at the implementation.
g.Go(func() error {
......
......@@ -32,6 +32,11 @@ type Constructor struct {
Args []interface{}
}
type SuperchainPredeploy struct {
Name string
CodeHash common.Hash
}
type Deployment struct {
Name string
Bytecode hexutil.Bytes
......
......@@ -437,6 +437,10 @@ func (d *DeployConfig) GetDeployedAddresses(hh *hardhat.Hardhat) error {
return nil
}
func (d *DeployConfig) GovernanceEnabled() bool {
return d.EnableGovernance
}
func (d *DeployConfig) RegolithTime(genesisTime uint64) *uint64 {
if d.L2GenesisRegolithTimeOffset == nil {
return nil
......
......@@ -18,13 +18,13 @@ var (
codeNamespace = common.HexToAddress("0xc0D3C0d3C0d3C0D3c0d3C0d3c0D3C0d3c0d30000")
// l2PredeployNamespace represents the namespace of L2 predeploys
l2PredeployNamespace = common.HexToAddress("0x4200000000000000000000000000000000000000")
// bigL2PredeployNamespace represents the predeploy namespace as a big.Int
// BigL2PredeployNamespace represents the predeploy namespace as a big.Int
BigL2PredeployNamespace = new(big.Int).SetBytes(l2PredeployNamespace.Bytes())
// bigCodeNamespace represents the predeploy namespace as a big.Int
bigCodeNameSpace = new(big.Int).SetBytes(codeNamespace.Bytes())
// implementationSlot represents the EIP 1967 implementation storage slot
bigCodeNamespace = new(big.Int).SetBytes(codeNamespace.Bytes())
// ImplementationSlot represents the EIP 1967 implementation storage slot
ImplementationSlot = common.HexToHash("0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc")
// implementationSlot represents the EIP 1967 admin storage slot
// AdminSlot represents the EIP 1967 admin storage slot
AdminSlot = common.HexToHash("0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103")
)
......@@ -69,7 +69,7 @@ func AddressToCodeNamespace(addr common.Address) (common.Address, error) {
return common.Address{}, fmt.Errorf("cannot handle non predeploy: %s", addr)
}
bigAddress := new(big.Int).SetBytes(addr[18:])
num := new(big.Int).Or(bigCodeNameSpace, bigAddress)
num := new(big.Int).Or(bigCodeNamespace, bigAddress)
return common.BigToAddress(num), nil
}
......
......@@ -13,7 +13,7 @@ import (
"github.com/ethereum-optimism/optimism/op-service/eth"
)
// BuildL2DeveloperGenesis will build the L2 genesis block.
// BuildL2Genesis will build the L2 genesis block.
func BuildL2Genesis(config *DeployConfig, l1StartBlock *types.Block) (*core.Genesis, error) {
genspec, err := NewL2Genesis(config, l1StartBlock)
if err != nil {
......@@ -50,25 +50,23 @@ func BuildL2Genesis(config *DeployConfig, l1StartBlock *types.Block) (*core.Gene
return nil, err
}
for name, predeploy := range predeploys.Predeploys {
addr := *predeploy
if addr == predeploys.GovernanceTokenAddr && !config.EnableGovernance {
// there is no governance token configured, so skip the governance token predeploy
log.Warn("Governance is not enabled, skipping governance token predeploy.")
if predeploy.Enabled != nil && !predeploy.Enabled(config) {
log.Warn("Skipping disabled predeploy.", "name", name, "address", predeploy.Address)
continue
}
codeAddr := addr
if predeploys.IsProxied(addr) {
codeAddr, err = AddressToCodeNamespace(addr)
codeAddr := predeploy.Address
if !predeploy.ProxyDisabled {
codeAddr, err = AddressToCodeNamespace(predeploy.Address)
if err != nil {
return nil, fmt.Errorf("error converting to code namespace: %w", err)
}
db.CreateAccount(codeAddr)
db.SetState(addr, ImplementationSlot, eth.AddressAsLeftPaddedHash(codeAddr))
log.Info("Set proxy", "name", name, "address", addr, "implementation", codeAddr)
} else {
db.DeleteState(addr, AdminSlot)
db.SetState(predeploy.Address, ImplementationSlot, eth.AddressAsLeftPaddedHash(codeAddr))
log.Info("Set proxy", "name", name, "address", predeploy.Address, "implementation", codeAddr)
} else if db.Exist(predeploy.Address) {
db.DeleteState(predeploy.Address, AdminSlot)
}
if err := setupPredeploy(db, deployResults, storage, name, addr, codeAddr); err != nil {
if err := setupPredeploy(db, deployResults, storage, name, predeploy.Address, codeAddr); err != nil {
return nil, err
}
code := db.GetCode(codeAddr)
......
......@@ -18,6 +18,7 @@ import (
"github.com/ethereum-optimism/optimism/op-bindings/bindings"
"github.com/ethereum-optimism/optimism/op-bindings/predeploys"
"github.com/ethereum-optimism/optimism/op-chain-ops/genesis"
"github.com/ethereum-optimism/optimism/op-chain-ops/immutables"
"github.com/ethereum-optimism/optimism/op-service/eth"
)
......@@ -48,14 +49,14 @@ func testBuildL2Genesis(t *testing.T, config *genesis.DeployConfig) *core.Genesi
require.NoError(t, err)
for name, predeploy := range predeploys.Predeploys {
addr := *predeploy
addr := predeploy.Address
account, ok := gen.Alloc[addr]
require.Equal(t, true, ok, name)
require.Greater(t, len(account.Code), 0)
adminSlot, ok := account.Storage[genesis.AdminSlot]
isProxy := predeploys.IsProxied(addr) ||
isProxy := !predeploy.ProxyDisabled ||
(!config.EnableGovernance && addr == predeploys.GovernanceTokenAddr)
if isProxy {
require.Equal(t, true, ok, name)
......@@ -73,6 +74,10 @@ func testBuildL2Genesis(t *testing.T, config *genesis.DeployConfig) *core.Genesi
require.Equal(t, common.Big1, gen.Alloc[addr].Balance)
}
create2Deployer := gen.Alloc[predeploys.Create2DeployerAddr]
codeHash := crypto.Keccak256Hash(create2Deployer.Code)
require.Equal(t, codeHash, immutables.Create2DeployerCodeHash)
if writeFile {
file, _ := json.MarshalIndent(gen, "", " ")
_ = os.WriteFile("genesis.json", file, 0644)
......@@ -86,7 +91,7 @@ func TestBuildL2MainnetGenesis(t *testing.T) {
config.EnableGovernance = true
config.FundDevAccounts = false
gen := testBuildL2Genesis(t, config)
require.Equal(t, 2322, len(gen.Alloc))
require.Equal(t, 2323, len(gen.Alloc))
}
func TestBuildL2MainnetNoGovernanceGenesis(t *testing.T) {
......@@ -95,5 +100,5 @@ func TestBuildL2MainnetNoGovernanceGenesis(t *testing.T) {
config.EnableGovernance = false
config.FundDevAccounts = false
gen := testBuildL2Genesis(t, config)
require.Equal(t, 2322, len(gen.Alloc))
require.Equal(t, 2323, len(gen.Alloc))
}
......@@ -55,8 +55,7 @@ func setProxies(db vm.StateDB, proxyAdminAddr common.Address, namespace *big.Int
}
// SetPrecompileBalances will set a single wei at each precompile address.
// This is an optimization to make calling them cheaper. This should only
// be used for devnets.
// This is an optimization to make calling them cheaper.
func SetPrecompileBalances(db vm.StateDB) {
for i := 0; i < PrecompileCount; i++ {
addr := common.BytesToAddress([]byte{byte(i)})
......
......@@ -41,5 +41,8 @@
"enableGovernance": true,
"governanceTokenSymbol": "OP",
"governanceTokenName": "Optimism",
"governanceTokenOwner": "0x0000000000000000000000000000000000000333"
"governanceTokenOwner": "0x0000000000000000000000000000000000000333",
"l2GenesisRegolithTimeOffset": "0x0",
"l2GenesisCanyonTimeOffset": "0x0"
}
......@@ -5,16 +5,17 @@ import (
"fmt"
"math/big"
"github.com/ethereum-optimism/superchain-registry/superchain"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum-optimism/optimism/op-bindings/bindings"
"github.com/ethereum-optimism/optimism/op-bindings/predeploys"
"github.com/ethereum-optimism/optimism/op-chain-ops/deployer"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
)
// ImmutableValues represents the values to be set in immutable code.
......@@ -26,6 +27,8 @@ type ImmutableValues map[string]any
// contracts.
type ImmutableConfig map[string]ImmutableValues
var Create2DeployerCodeHash = common.HexToHash("0xb0550b5b431e30d38000efb7107aaa0ade03d48a7198a140edda9d27134468b2")
// Check does a sanity check that the specific values that
// Optimism uses are set inside of the ImmutableConfig.
func (i ImmutableConfig) Check() error {
......@@ -152,13 +155,19 @@ func BuildOptimism(immutable ImmutableConfig) (DeploymentResults, error) {
Name: "SchemaRegistry",
},
}
return BuildL2(deployments)
superchainPredeploys := []deployer.SuperchainPredeploy{
{
Name: "Create2Deployer",
CodeHash: Create2DeployerCodeHash,
},
}
return BuildL2(deployments, superchainPredeploys)
}
// BuildL2 will deploy contracts to a simulated backend so that their immutables
// can be properly set. The bytecode returned in the results is suitable to be
// inserted into the state via state surgery.
func BuildL2(constructors []deployer.Constructor) (DeploymentResults, error) {
func BuildL2(constructors []deployer.Constructor, superchainPredeploys []deployer.SuperchainPredeploy) (DeploymentResults, error) {
log.Info("Creating L2 state")
deployments, err := deployer.Deploy(deployer.NewL2Backend(), constructors, l2Deployer)
if err != nil {
......@@ -168,6 +177,13 @@ func BuildL2(constructors []deployer.Constructor) (DeploymentResults, error) {
for _, dep := range deployments {
results[dep.Name] = dep.Bytecode
}
for _, dep := range superchainPredeploys {
code, err := superchain.LoadContractBytecode(superchain.Hash(dep.CodeHash))
if err != nil {
return nil, err
}
results[dep.Name] = code
}
return results, nil
}
......
......@@ -65,6 +65,7 @@ func TestBuildOptimism(t *testing.T) {
"LegacyERC20ETH": true,
"EAS": true,
"SchemaRegistry": true,
"Create2Deployer": true,
}
// Only the exact contracts that we care about are being
......
......@@ -61,6 +61,10 @@ func (db *MemoryStateDB) CreateAccount(addr common.Address) {
db.rw.Lock()
defer db.rw.Unlock()
db.createAccount(addr)
}
func (db *MemoryStateDB) createAccount(addr common.Address) {
if _, ok := db.genesis.Alloc[addr]; !ok {
db.genesis.Alloc[addr] = core.GenesisAccount{
Code: []byte{},
......@@ -69,7 +73,6 @@ func (db *MemoryStateDB) CreateAccount(addr common.Address) {
Nonce: 0,
}
}
}
func (db *MemoryStateDB) SubBalance(addr common.Address, amount *big.Int) {
......@@ -165,6 +168,8 @@ func (db *MemoryStateDB) SetCode(addr common.Address, code []byte) {
db.rw.Lock()
defer db.rw.Unlock()
db.createAccount(addr)
account, ok := db.genesis.Alloc[addr]
if !ok {
return
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment