Commit d19ebe01 authored by Roberto Bayardo's avatar Roberto Bayardo

make governance token predeploy optional

add non-legacy L2 superchain mainnet deploy utility
parent 2959bbb2
...@@ -46,6 +46,29 @@ var ( ...@@ -46,6 +46,29 @@ var (
Predeploys = make(map[string]*common.Address) Predeploys = make(map[string]*common.Address)
) )
// IsProxied returns true for predeploys that will sit behind a proxy contract
func IsProxied(predeployAddr common.Address) bool {
switch predeployAddr {
case LegacyERC20ETHAddr:
return false
case WETH9Addr:
return false
case GovernanceTokenAddr:
return false
case ProxyAdminAddr:
return false
default:
return true
}
}
// IsDeprecated returns true for predeploys we should skip in post-bedrock genesis generation
func IsDeprecated(predeployAddr common.Address) bool {
// TODO: confirm if we can safely add the remaining deprecated predeploys here
// (see https://github.com/ethereum-optimism/optimism/blob/develop/specs/predeploys.md#overview)
return predeployAddr == LegacyERC20ETHAddr
}
func init() { func init() {
Predeploys["L2ToL1MessagePasser"] = &L2ToL1MessagePasserAddr Predeploys["L2ToL1MessagePasser"] = &L2ToL1MessagePasserAddr
Predeploys["DeployerWhitelist"] = &DeployerWhitelistAddr Predeploys["DeployerWhitelist"] = &DeployerWhitelistAddr
......
...@@ -240,15 +240,16 @@ func (d *DeployConfig) Check() error { ...@@ -240,15 +240,16 @@ func (d *DeployConfig) Check() error {
if d.L2GenesisBlockBaseFeePerGas == nil { if d.L2GenesisBlockBaseFeePerGas == nil {
return fmt.Errorf("%w: L2 genesis block base fee per gas cannot be nil", ErrInvalidDeployConfig) return fmt.Errorf("%w: L2 genesis block base fee per gas cannot be nil", ErrInvalidDeployConfig)
} }
if d.GovernanceTokenName == "" { if d.GovernanceTokenName != "" {
return fmt.Errorf("%w: GovernanceToken.name cannot be empty", ErrInvalidDeployConfig)
}
if d.GovernanceTokenSymbol == "" { if d.GovernanceTokenSymbol == "" {
return fmt.Errorf("%w: GovernanceToken.symbol cannot be empty", ErrInvalidDeployConfig) return fmt.Errorf("%w: GovernanceToken.symbol cannot be empty", ErrInvalidDeployConfig)
} }
if d.GovernanceTokenOwner == (common.Address{}) { if d.GovernanceTokenOwner == (common.Address{}) {
return fmt.Errorf("%w: GovernanceToken owner cannot be address(0)", ErrInvalidDeployConfig) return fmt.Errorf("%w: GovernanceToken owner cannot be address(0)", ErrInvalidDeployConfig)
} }
} else if d.GovernanceTokenSymbol != "" || d.GovernanceTokenOwner != (common.Address{}) {
return fmt.Errorf("%w: Governance token fields must be either all specified or all empty", ErrInvalidDeployConfig)
}
return nil return nil
} }
...@@ -492,11 +493,13 @@ func NewL2StorageConfig(config *DeployConfig, block *types.Block) (state.Storage ...@@ -492,11 +493,13 @@ func NewL2StorageConfig(config *DeployConfig, block *types.Block) (state.Storage
"symbol": "WETH", "symbol": "WETH",
"decimals": 18, "decimals": 18,
} }
if len(config.GovernanceTokenName) != 0 {
storage["GovernanceToken"] = state.StorageValues{ storage["GovernanceToken"] = state.StorageValues{
"_name": config.GovernanceTokenName, "_name": config.GovernanceTokenName,
"_symbol": config.GovernanceTokenSymbol, "_symbol": config.GovernanceTokenSymbol,
"_owner": config.GovernanceTokenOwner, "_owner": config.GovernanceTokenOwner,
} }
}
storage["ProxyAdmin"] = state.StorageValues{ storage["ProxyAdmin"] = state.StorageValues{
"_owner": config.ProxyAdminOwner, "_owner": config.ProxyAdminOwner,
} }
......
package genesis package genesis
import ( import (
"github.com/ethereum-optimism/optimism/op-chain-ops/state" "errors"
"github.com/ethereum/go-ethereum/core/types" "fmt"
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum-optimism/optimism/op-bindings/bindings"
"github.com/ethereum-optimism/optimism/op-bindings/predeploys"
"github.com/ethereum-optimism/optimism/op-chain-ops/immutables"
"github.com/ethereum-optimism/optimism/op-chain-ops/state"
) )
// BuildL2DeveloperGenesis will build the developer Optimism Genesis // BuildL2DeveloperGenesis will build the developer Optimism Genesis
...@@ -46,3 +54,74 @@ func BuildL2DeveloperGenesis(config *DeployConfig, l1StartBlock *types.Block) (* ...@@ -46,3 +54,74 @@ func BuildL2DeveloperGenesis(config *DeployConfig, l1StartBlock *types.Block) (*
return db.Genesis(), nil return db.Genesis(), nil
} }
// BuildL2MainnetGenesis will build an L2 Genesis suitable for a Superchain mainnet that does not
// require a pre-bedrock migration & supports optional governance token predeploy.
func BuildL2MainnetGenesis(config *DeployConfig, l1StartBlock *types.Block) (*core.Genesis, error) {
genspec, err := NewL2Genesis(config, l1StartBlock)
if err != nil {
return nil, err
}
db := state.NewMemoryStateDB(genspec)
storage, err := NewL2StorageConfig(config, l1StartBlock)
if err != nil {
return nil, err
}
immutable, err := NewL2ImmutableConfig(config, l1StartBlock)
if err != nil {
return nil, err
}
// Set up the proxies
depBytecode, err := bindings.GetDeployedBytecode("Proxy")
if err != nil {
return nil, err
}
if len(depBytecode) == 0 {
return nil, errors.New("Proxy has empty bytecode")
}
for i := uint64(0); i <= 2048; i++ {
bigAddr := new(big.Int).Or(bigL2PredeployNamespace, new(big.Int).SetUint64(i))
addr := common.BigToAddress(bigAddr)
db.CreateAccount(addr)
db.SetCode(addr, depBytecode)
db.SetState(addr, AdminSlot, predeploys.ProxyAdminAddr.Hash())
}
// Set up the implementations
deployResults, err := immutables.BuildOptimism(immutable)
if err != nil {
return nil, err
}
for name, predeploy := range predeploys.Predeploys {
addr := *predeploy
if predeploys.IsDeprecated(addr) {
continue
}
if addr == predeploys.GovernanceTokenAddr && storage["GovernanceToken"] == nil {
// there is no governance token configured, so skip the governance token predeploy
continue
}
codeAddr := addr
if predeploys.IsProxied(addr) {
codeAddr, err = AddressToCodeNamespace(addr)
if err != nil {
return nil, fmt.Errorf("error converting to code namespace: %w", err)
}
db.CreateAccount(codeAddr)
}
db.SetState(addr, ImplementationSlot, codeAddr.Hash())
if err := setupPredeploy(db, deployResults, storage, name, addr, codeAddr); err != nil {
return nil, err
}
code := db.GetCode(codeAddr)
if len(code) == 0 {
return nil, fmt.Errorf("code not set for %s", name)
}
}
return db.Genesis(), nil
}
...@@ -8,17 +8,16 @@ import ( ...@@ -8,17 +8,16 @@ import (
"os" "os"
"testing" "testing"
"github.com/ethereum-optimism/optimism/op-bindings/bindings" "github.com/stretchr/testify/require"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends" "github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum-optimism/optimism/op-bindings/bindings"
"github.com/ethereum-optimism/optimism/op-bindings/predeploys" "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/genesis"
"github.com/stretchr/testify/require"
) )
var writeFile bool var writeFile bool
...@@ -53,7 +52,7 @@ func TestBuildL2DeveloperGenesis(t *testing.T) { ...@@ -53,7 +52,7 @@ func TestBuildL2DeveloperGenesis(t *testing.T) {
addr := *address addr := *address
account, ok := gen.Alloc[addr] account, ok := gen.Alloc[addr]
require.Equal(t, ok, true) require.Equal(t, true, ok)
require.Greater(t, len(account.Code), 0) require.Greater(t, len(account.Code), 0)
if name == "GovernanceToken" || name == "LegacyERC20ETH" || name == "ProxyAdmin" || name == "WETH9" { if name == "GovernanceToken" || name == "LegacyERC20ETH" || name == "ProxyAdmin" || name == "WETH9" {
...@@ -61,9 +60,9 @@ func TestBuildL2DeveloperGenesis(t *testing.T) { ...@@ -61,9 +60,9 @@ func TestBuildL2DeveloperGenesis(t *testing.T) {
} }
adminSlot, ok := account.Storage[genesis.AdminSlot] adminSlot, ok := account.Storage[genesis.AdminSlot]
require.Equal(t, ok, true) require.Equal(t, true, ok)
require.Equal(t, adminSlot, predeploys.ProxyAdminAddr.Hash()) require.Equal(t, predeploys.ProxyAdminAddr.Hash(), adminSlot)
require.Equal(t, account.Code, depB) require.Equal(t, depB, account.Code)
} }
require.Equal(t, 2343, len(gen.Alloc)) require.Equal(t, 2343, len(gen.Alloc))
...@@ -94,3 +93,110 @@ func TestBuildL2DeveloperGenesisDevAccountsFunding(t *testing.T) { ...@@ -94,3 +93,110 @@ func TestBuildL2DeveloperGenesisDevAccountsFunding(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, 2321, len(gen.Alloc)) require.Equal(t, 2321, len(gen.Alloc))
} }
func TestBuildL2MainnetGenesis(t *testing.T) {
config, err := genesis.NewDeployConfig("./testdata/test-deploy-config-devnet-l1.json")
require.Nil(t, err)
backend := backends.NewSimulatedBackend(
core.GenesisAlloc{
crypto.PubkeyToAddress(testKey.PublicKey): {Balance: big.NewInt(10000000000000000)},
},
15000000,
)
block, err := backend.BlockByNumber(context.Background(), common.Big0)
require.NoError(t, err)
gen, err := genesis.BuildL2MainnetGenesis(config, block)
require.Nil(t, err)
require.NotNil(t, gen)
depB, err := bindings.GetDeployedBytecode("Proxy")
require.NoError(t, err)
for name, predeploy := range predeploys.Predeploys {
addr := *predeploy
if predeploys.IsDeprecated(addr) {
continue
}
account, ok := gen.Alloc[addr]
if predeploys.IsDeprecated(addr) && !predeploys.IsProxied(addr) {
require.Equal(t, false, ok, name)
continue
}
require.Equal(t, true, ok, name)
require.Greater(t, len(account.Code), 0)
if !predeploys.IsProxied(addr) {
continue
}
adminSlot, ok := account.Storage[genesis.AdminSlot]
require.Equal(t, true, ok)
require.Equal(t, predeploys.ProxyAdminAddr.Hash(), adminSlot)
require.Equal(t, depB, account.Code)
}
require.Equal(t, 2063, len(gen.Alloc)) // TODO: confirm 2063 is correct!
if writeFile {
file, _ := json.MarshalIndent(gen, "", " ")
_ = os.WriteFile("genesis.json", file, 0644)
}
}
// Same test as TestBuildL2MainnetGenesis, only we blow away the governance token config and
// confirm the governance predeploy doesn't exist (or more precisely, there's an unused proxy
// contract at its address instead).
func TestBuildL2MainnetNoGovernanceGenesis(t *testing.T) {
config, err := genesis.NewDeployConfig("./testdata/test-deploy-config-devnet-l1.json")
require.Nil(t, err)
config.GovernanceTokenSymbol = ""
config.GovernanceTokenName = ""
config.GovernanceTokenOwner = common.Address{}
backend := backends.NewSimulatedBackend(
core.GenesisAlloc{
crypto.PubkeyToAddress(testKey.PublicKey): {Balance: big.NewInt(10000000000000000)},
},
15000000,
)
block, err := backend.BlockByNumber(context.Background(), common.Big0)
require.NoError(t, err)
gen, err := genesis.BuildL2MainnetGenesis(config, block)
require.Nil(t, err)
require.NotNil(t, gen)
depB, err := bindings.GetDeployedBytecode("Proxy")
require.NoError(t, err)
for name, predeploy := range predeploys.Predeploys {
addr := *predeploy
account, ok := gen.Alloc[addr]
if predeploys.IsDeprecated(addr) && !predeploys.IsProxied(addr) {
require.Equal(t, false, ok, name)
continue
}
require.Equal(t, true, ok, name)
require.Greater(t, len(account.Code), 0)
if !predeploys.IsProxied(addr) && addr != predeploys.GovernanceTokenAddr {
continue
}
adminSlot, ok := account.Storage[genesis.AdminSlot]
require.Equal(t, true, ok)
require.Equal(t, predeploys.ProxyAdminAddr.Hash(), adminSlot)
require.Equal(t, depB, account.Code)
}
require.Equal(t, 2063, len(gen.Alloc)) // TODO: confirm 2063 is correct!
if writeFile {
file, _ := json.MarshalIndent(gen, "", " ")
_ = os.WriteFile("genesis.json", file, 0644)
}
}
...@@ -35,5 +35,9 @@ ...@@ -35,5 +35,9 @@
"l1CrossDomainMessengerProxy": "0xff000000000000000000000000000000000000dd", "l1CrossDomainMessengerProxy": "0xff000000000000000000000000000000000000dd",
"deploymentWaitConfirmations": 1, "deploymentWaitConfirmations": 1,
"fundDevAccounts": true "fundDevAccounts": true,
"governanceTokenSymbol": "OP",
"governanceTokenName": "Optimism",
"governanceTokenOwner": "0x0000000000000000000000000000000000000333"
} }
...@@ -416,11 +416,9 @@ export const deployConfigSpec: { ...@@ -416,11 +416,9 @@ export const deployConfigSpec: {
}, },
governanceTokenSymbol: { governanceTokenSymbol: {
type: 'string', type: 'string',
default: 'OP',
}, },
governanceTokenName: { governanceTokenName: {
type: 'string', type: 'string',
default: 'Optimism',
}, },
governanceTokenOwner: { governanceTokenOwner: {
type: 'string', type: 'string',
......
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