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

op-deployer: Run validations over L2 genesis #12560

Merged
merged 4 commits into from
Oct 22, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
127 changes: 106 additions & 21 deletions op-deployer/pkg/deployer/integration_test/apply_test.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
package integration_test

import (
"bytes"
"context"
"encoding/hex"
"fmt"
"log/slog"
"math/big"
"net/url"
"os"
"path"
"runtime"
"strings"
"testing"
"time"

Expand Down Expand Up @@ -107,7 +108,7 @@ func TestEndToEndApply(t *testing.T) {
deployerAddr, err := dk.Address(depKey)
require.NoError(t, err)

loc := localArtifacsLocator(t)
loc := localArtifactsLocator(t)

bcaster, err := broadcaster.NewKeyedBroadcaster(broadcaster.KeyedBroadcasterOpts{
Logger: log.NewLogger(log.DiscardHandler()),
Expand Down Expand Up @@ -153,7 +154,7 @@ func TestEndToEndApply(t *testing.T) {
})
}

func localArtifacsLocator(t *testing.T) *opcm.ArtifactsLocator {
func localArtifactsLocator(t *testing.T) *opcm.ArtifactsLocator {
_, testFilename, _, ok := runtime.Caller(0)
require.Truef(t, ok, "failed to get test filename")
monorepoDir := path.Join(path.Dir(testFilename), "..", "..", "..", "..")
Expand Down Expand Up @@ -370,6 +371,7 @@ func validateOPChainDeployment(t *testing.T, cg codeGetter, st *state.State, int
checkImmutable(t, alloc, predeploys.BaseFeeVaultAddr, chainIntent.BaseFeeVaultRecipient)
checkImmutable(t, alloc, predeploys.L1FeeVaultAddr, chainIntent.L1FeeVaultRecipient)
checkImmutable(t, alloc, predeploys.SequencerFeeVaultAddr, chainIntent.SequencerFeeVaultRecipient)
checkImmutable(t, alloc, predeploys.OptimismMintableERC721FactoryAddr, common.BigToHash(new(big.Int).SetUint64(intent.L1ChainID)))

// ownership slots
var addrAsSlot common.Hash
Expand All @@ -393,16 +395,19 @@ func getEIP1967ImplementationAddress(t *testing.T, allocations types.GenesisAllo
return common.HexToAddress(storageValue.Hex())
}

func checkImmutable(t *testing.T, allocations types.GenesisAlloc, proxyContract common.Address, feeRecipient common.Address) {
type bytesMarshaler interface {
Bytes() []byte
}

func checkImmutable(t *testing.T, allocations types.GenesisAlloc, proxyContract common.Address, thing bytesMarshaler) {
implementationAddress := getEIP1967ImplementationAddress(t, allocations, proxyContract)
account, ok := allocations[implementationAddress]
require.True(t, ok, "%s not found in allocations", implementationAddress.Hex())
require.NotEmpty(t, account.Code, "%s should have code", implementationAddress.Hex())
require.Contains(
require.True(t, ok, "%s not found in allocations", implementationAddress)
require.NotEmpty(t, account.Code, "%s should have code", implementationAddress)
require.True(
t,
strings.ToLower(common.Bytes2Hex(account.Code)),
strings.ToLower(strings.TrimPrefix(feeRecipient.Hex(), "0x")),
"%s code should contain %s immutable", implementationAddress.Hex(), feeRecipient.Hex(),
bytes.Contains(account.Code, thing.Bytes()),
"%s code should contain %s immutable", implementationAddress, hex.EncodeToString(thing.Bytes()),
)
}

Expand Down Expand Up @@ -520,7 +525,7 @@ func TestL2BlockTimeOverride(t *testing.T) {
deployerAddr, err := dk.Address(depKey)
require.NoError(t, err)

loc := localArtifacsLocator(t)
loc := localArtifactsLocator(t)

bcaster, err := broadcaster.NewKeyedBroadcaster(broadcaster.KeyedBroadcasterOpts{
Logger: lgr,
Expand Down Expand Up @@ -554,7 +559,11 @@ func TestL2BlockTimeOverride(t *testing.T) {
st,
))

cfg, err := state.CombineDeployConfig(intent, &state.ChainIntent{}, st, st.Chains[0])
chainIntent, err := intent.Chain(l2ChainID.Bytes32())
require.NoError(t, err)
chainState, err := st.Chain(l2ChainID.Bytes32())
require.NoError(t, err)
cfg, err := state.CombineDeployConfig(intent, chainIntent, st, chainState)
require.NoError(t, err)

require.Equal(t, uint64(3), cfg.L2InitializationConfig.L2CoreDeployConfig.L2BlockTime, "L2 block time should be 3 seconds")
Expand All @@ -579,15 +588,7 @@ func TestApplyGenesisStrategy(t *testing.T) {
deployerAddr, err := dk.Address(depKey)
require.NoError(t, err)

_, testFilename, _, ok := runtime.Caller(0)
require.Truef(t, ok, "failed to get test filename")
monorepoDir := path.Join(path.Dir(testFilename), "..", "..", "..", "..")
artifactsDir := path.Join(monorepoDir, "packages", "contracts-bedrock", "forge-artifacts")
artifactsURL, err := url.Parse(fmt.Sprintf("file://%s", artifactsDir))
require.NoError(t, err)
loc := &opcm.ArtifactsLocator{
URL: artifactsURL,
}
loc := localArtifactsLocator(t)

env, bundle, _ := createEnv(t, ctx, lgr, nil, broadcaster.NoopBroadcaster(), deployerAddr)
intent, st := newIntent(t, l1ChainID, dk, l2ChainID1, loc, loc)
Expand All @@ -611,3 +612,87 @@ func TestApplyGenesisStrategy(t *testing.T) {
})
}
}

func TestInvalidL2Genesis(t *testing.T) {
op_e2e.InitParallel(t)

lgr := testlog.Logger(t, slog.LevelDebug)

ctx, cancel := context.WithCancel(context.Background())
defer cancel()

depKey := new(deployerKey)
l1ChainID := big.NewInt(77799777)
dk, err := devkeys.NewMnemonicDevKeys(devkeys.TestMnemonic)
require.NoError(t, err)

l2ChainID1 := uint256.NewInt(1)

deployerAddr, err := dk.Address(depKey)
require.NoError(t, err)

loc := localArtifactsLocator(t)

// these tests were generated by grepping all usages of the deploy
// config in L2Genesis.s.sol.
tests := []struct {
name string
overrides map[string]any
}{
{
name: "proxy admin owner not set",
mslipper marked this conversation as resolved.
Show resolved Hide resolved
overrides: map[string]any{
"proxyAdminOwner": nil,
},
},
{
name: "base fee vault recipient not set",
overrides: map[string]any{
"baseFeeVaultRecipient": nil,
},
},
{
name: "l1 fee vault recipient not set",
overrides: map[string]any{
"l1FeeVaultRecipient": nil,
},
},
{
name: "sequencer fee vault recipient not set",
overrides: map[string]any{
"sequencerFeeVaultRecipient": nil,
},
},
{
name: "l1 chain ID not set",
overrides: map[string]any{
"l1ChainID": nil,
},
},
{
name: "l2 chain ID not set",
overrides: map[string]any{
"l2ChainID": nil,
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
env, bundle, _ := createEnv(t, ctx, lgr, nil, broadcaster.NoopBroadcaster(), deployerAddr)
intent, st := newIntent(t, l1ChainID, dk, l2ChainID1, loc, loc)
intent.Chains = append(intent.Chains, newChainIntent(t, dk, l1ChainID, l2ChainID1))
intent.DeploymentStrategy = state.DeploymentStrategyGenesis
intent.GlobalDeployOverrides = tt.overrides

err := deployer.ApplyPipeline(
ctx,
env,
bundle,
intent,
st,
)
require.Error(t, err)
require.ErrorContains(t, err, "failed to combine L2 init config")
})
}
}
72 changes: 48 additions & 24 deletions op-deployer/pkg/deployer/state/deploy_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ import (
"fmt"
"math/big"

"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/rpc"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"

Expand All @@ -18,8 +21,16 @@ var (
vaultMinWithdrawalAmount = mustHexBigFromHex("0x8ac7230489e80000")
)

func DefaultDeployConfig(chainIntent *ChainIntent) genesis.DeployConfig {
return genesis.DeployConfig{
func CombineDeployConfig(intent *Intent, chainIntent *ChainIntent, state *State, chainState *ChainState) (genesis.DeployConfig, error) {
cfg := genesis.DeployConfig{
L1DependenciesConfig: genesis.L1DependenciesConfig{
L1StandardBridgeProxy: chainState.L1StandardBridgeProxyAddress,
L1CrossDomainMessengerProxy: chainState.L1CrossDomainMessengerProxyAddress,
L1ERC721BridgeProxy: chainState.L1ERC721BridgeProxyAddress,
SystemConfigProxy: chainState.SystemConfigProxyAddress,
OptimismPortalProxy: chainState.OptimismPortalProxyAddress,
ProtocolVersionsProxy: state.SuperchainDeployment.ProtocolVersionsProxyAddress,
},
L2InitializationConfig: genesis.L2InitializationConfig{
L2GenesisBlockDeployConfig: genesis.L2GenesisBlockDeployConfig{
L2GenesisBlockGasLimit: 60_000_000,
Expand Down Expand Up @@ -61,28 +72,57 @@ func DefaultDeployConfig(chainIntent *ChainIntent) genesis.DeployConfig {
UseInterop: false,
},
L2CoreDeployConfig: genesis.L2CoreDeployConfig{
L1ChainID: intent.L1ChainID,
L2ChainID: chainState.ID.Big().Uint64(),
L2BlockTime: 2,
FinalizationPeriodSeconds: 12,
MaxSequencerDrift: 600,
SequencerWindowSize: 3600,
ChannelTimeoutBedrock: 300,
SystemConfigStartBlock: 0,
BatchInboxAddress: calculateBatchInboxAddr(chainState.ID),
},
OperatorDeployConfig: genesis.OperatorDeployConfig{
BatchSenderAddress: chainIntent.Roles.Batcher,
P2PSequencerAddress: chainIntent.Roles.UnsafeBlockSigner,
},
OwnershipDeployConfig: genesis.OwnershipDeployConfig{
ProxyAdminOwner: chainIntent.Roles.L2ProxyAdminOwner,
FinalSystemOwner: chainIntent.Roles.L1ProxyAdminOwner,
},
},
FaultProofDeployConfig: genesis.FaultProofDeployConfig{
UseFaultProofs: true,
FaultGameWithdrawalDelay: 604800,
PreimageOracleMinProposalSize: 126000,
PreimageOracleChallengePeriod: 86400,
ProofMaturityDelaySeconds: 604800,
DisputeGameFinalityDelaySeconds: 302400,
},
}
}

func CombineDeployConfig(intent *Intent, chainIntent *ChainIntent, state *State, chainState *ChainState) (genesis.DeployConfig, error) {
firstChainIntent := intent.Chains[0]
cfg := DefaultDeployConfig(firstChainIntent)
// The below dummy variables are set in order to allow the deploy
// config to pass validation. The validation checks are useful to
// ensure that the L2 is properly configured. They are not used by
// the L2 genesis script itself.
num := rpc.LatestBlockNumber
cfg.L1StartingBlockTag = &genesis.MarshalableRPCBlockNumberOrHash{
BlockNumber: &num,
}
cfg.L1BlockTime = 12
dummyAddr := common.Address{19: 0x01}
cfg.SuperchainL1DeployConfig = genesis.SuperchainL1DeployConfig{
SuperchainConfigGuardian: dummyAddr,
}
cfg.OutputOracleDeployConfig = genesis.OutputOracleDeployConfig{
L2OutputOracleSubmissionInterval: 1,
L2OutputOracleStartingTimestamp: 1,
L2OutputOracleProposer: dummyAddr,
L2OutputOracleChallenger: dummyAddr,
}
// End of dummy variables

// Apply overrides after setting the main values.
var err error
if len(intent.GlobalDeployOverrides) > 0 {
cfg, err = mergeJSON(cfg, intent.GlobalDeployOverrides)
Expand All @@ -99,25 +139,9 @@ func CombineDeployConfig(intent *Intent, chainIntent *ChainIntent, state *State,
}
}

cfg.L2ChainID = chainState.ID.Big().Uint64()
cfg.L1DependenciesConfig = genesis.L1DependenciesConfig{
L1StandardBridgeProxy: chainState.L1StandardBridgeProxyAddress,
L1CrossDomainMessengerProxy: chainState.L1CrossDomainMessengerProxyAddress,
L1ERC721BridgeProxy: chainState.L1ERC721BridgeProxyAddress,
SystemConfigProxy: chainState.SystemConfigProxyAddress,
OptimismPortalProxy: chainState.OptimismPortalProxyAddress,
ProtocolVersionsProxy: state.SuperchainDeployment.ProtocolVersionsProxyAddress,
}
cfg.OperatorDeployConfig = genesis.OperatorDeployConfig{
BatchSenderAddress: chainIntent.Roles.Batcher,
P2PSequencerAddress: chainIntent.Roles.UnsafeBlockSigner,
}
cfg.OwnershipDeployConfig = genesis.OwnershipDeployConfig{
ProxyAdminOwner: chainIntent.Roles.L2ProxyAdminOwner,
FinalSystemOwner: chainIntent.Roles.L1ProxyAdminOwner,
if err := cfg.Check(log.New(log.DiscardHandler())); err != nil {
return cfg, fmt.Errorf("combined deploy config failed validation: %w", err)
}
cfg.BatchInboxAddress = calculateBatchInboxAddr(chainState.ID)
cfg.L1ChainID = intent.L1ChainID

return cfg, nil
}
Expand Down