Commit 1095a392 authored by Michael de Hoog's avatar Michael de Hoog

Generalize superchain predeploys

parent df2349bd
......@@ -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
}
......@@ -89,8 +89,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
}
......@@ -102,7 +102,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)
......@@ -121,10 +121,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
......
......@@ -433,6 +433,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
......
......@@ -10,7 +10,6 @@ import (
"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/core/vm"
"github.com/ethereum/go-ethereum/rpc"
)
......@@ -19,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 represents the predeploy namespace as a big.Int
bigCodeNameSpace = new(big.Int).SetBytes(codeNamespace.Bytes())
// implementationSlot represents the EIP 1967 implementation storage slot
// 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")
)
......@@ -108,13 +107,3 @@ func newHexBig(in uint64) *hexutil.Big {
hb := hexutil.Big(*b)
return &hb
}
// CreateAccountOnSetCode is a vm.StateDB wrapper that optimistically creates an account on SetCode
type CreateAccountOnSetCode struct {
vm.StateDB
}
func (s *CreateAccountOnSetCode) SetCode(addr common.Address, code []byte) {
s.CreateAccount(addr)
s.StateDB.SetCode(addr, code)
}
......@@ -3,7 +3,6 @@ package genesis
import (
"fmt"
"github.com/ethereum/go-ethereum/consensus/misc"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log"
......@@ -14,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 {
......@@ -51,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)
......@@ -78,7 +75,5 @@ func BuildL2Genesis(config *DeployConfig, l1StartBlock *types.Block) (*core.Gene
}
}
misc.EnsureCreate2Deployer(genspec.Config, 0, &CreateAccountOnSetCode{db})
return db.Genesis(), nil
}
......@@ -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,9 +74,9 @@ func testBuildL2Genesis(t *testing.T, config *genesis.DeployConfig) *core.Genesi
require.Equal(t, common.Big1, gen.Alloc[addr].Balance)
}
create2Deployer := gen.Alloc[common.HexToAddress("0x13b0D85CcB8bf860b6b79AF3029fCA081AE9beF2")]
create2Deployer := gen.Alloc[predeploys.Create2DeployerAddr]
codeHash := crypto.Keccak256Hash(create2Deployer.Code)
require.Equal(t, codeHash, common.HexToHash("0xb0550b5b431e30d38000efb7107aaa0ade03d48a7198a140edda9d27134468b2"))
require.Equal(t, codeHash, immutables.Create2DeployerCodeHash)
if writeFile {
file, _ := json.MarshalIndent(gen, "", " ")
......
......@@ -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)})
......
......@@ -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 {
......@@ -151,13 +154,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 {
......@@ -167,6 +176,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