diff --git a/kurtosis-devnet/fileserver/main.star b/kurtosis-devnet/fileserver/main.star index c26357f1a3b..94b5dff887d 100644 --- a/kurtosis-devnet/fileserver/main.star +++ b/kurtosis-devnet/fileserver/main.star @@ -12,18 +12,22 @@ def get_used_ports(): return used_ports -def run(plan, source_path): +def run(plan, source_path, server_image=FILESERVER_IMAGE): service_name = "fileserver" config = get_fileserver_config( - plan, - service_name, - source_path, + plan = plan, + service_name = service_name, + source_path = source_path, + server_image = server_image, + ) + plan.add_service( + name = service_name, + config = config, ) - service = plan.add_service(service_name, config) return service_name -def get_fileserver_config(plan, service_name, source_path): +def get_fileserver_config(plan, service_name, source_path, server_image): files = {} # Upload content to container @@ -42,7 +46,7 @@ def get_fileserver_config(plan, service_name, source_path): ports = get_used_ports() return ServiceConfig( - image=FILESERVER_IMAGE, + image=server_image, ports=ports, cmd=["nginx", "-g", "daemon off;"], files=files, diff --git a/kurtosis-devnet/pkg/deploy/deploy.go b/kurtosis-devnet/pkg/deploy/deploy.go index 6bbf328f9a4..442edb697dd 100644 --- a/kurtosis-devnet/pkg/deploy/deploy.go +++ b/kurtosis-devnet/pkg/deploy/deploy.go @@ -240,12 +240,14 @@ func (d *Deployer) Deploy(ctx context.Context, r io.Reader) (*kurtosis.KurtosisE deployer: d.ktDeployer, } + ch := srv.getState(ctx) + buf, err := d.renderTemplate(tmpDir, srv.URL) if err != nil { return nil, fmt.Errorf("error rendering template: %w", err) } - if err := srv.Deploy(ctx, tmpDir); err != nil { + if err := srv.Deploy(ctx, tmpDir, ch); err != nil { return nil, fmt.Errorf("error deploying fileserver: %w", err) } diff --git a/kurtosis-devnet/pkg/deploy/fileserver.go b/kurtosis-devnet/pkg/deploy/fileserver.go index 84725f2039e..6040395b851 100644 --- a/kurtosis-devnet/pkg/deploy/fileserver.go +++ b/kurtosis-devnet/pkg/deploy/fileserver.go @@ -3,11 +3,17 @@ package deploy import ( "bytes" "context" + "crypto/sha256" + "encoding/hex" + "encoding/json" "fmt" + "log" "os" "path/filepath" "strings" + "sync" + ktfs "github.com/ethereum-optimism/optimism/devnet-sdk/kt/fs" "github.com/ethereum-optimism/optimism/kurtosis-devnet/pkg/kurtosis" "github.com/ethereum-optimism/optimism/kurtosis-devnet/pkg/util" ) @@ -25,17 +31,43 @@ func (f *FileServer) URL(path ...string) string { return fmt.Sprintf("http://%s/%s", FILESERVER_PACKAGE, strings.Join(path, "/")) } -func (f *FileServer) Deploy(ctx context.Context, sourceDir string) error { +func (f *FileServer) Deploy(ctx context.Context, sourceDir string, stateCh <-chan *fileserverState) error { + // Check if source directory is empty. If it is, then ie means we don't have + // anything to serve, so we might as well not deploy the fileserver. + entries, err := os.ReadDir(sourceDir) + if err != nil { + return fmt.Errorf("error reading source directory: %w", err) + } + if len(entries) == 0 { + return nil + } + + srcHash, err := calculateDirHash(sourceDir) + if err != nil { + return fmt.Errorf("error calculating source directory hash: %w", err) + } // Create a temp dir in the fileserver package baseDir := filepath.Join(f.baseDir, FILESERVER_PACKAGE) if err := os.MkdirAll(baseDir, 0755); err != nil { return fmt.Errorf("error creating base directory: %w", err) } + + configHash, err := calculateDirHash(filepath.Join(baseDir, "static_files", "nginx")) + if err != nil { + return fmt.Errorf("error calculating base directory hash: %w", err) + } + + refState := <-stateCh + if refState.contentHash == srcHash && refState.configHash == configHash { + log.Println("No changes to fileserver, skipping deployment") + return nil + } + // Can't use MkdirTemp here because the directory name needs to always be the same // in order for kurtosis file artifact upload to be idempotent. // (i.e. the file upload and all its downstream dependencies can be SKIPPED on re-runs) tempDir := filepath.Join(baseDir, "upload-content") - err := os.Mkdir(tempDir, 0755) + err = os.Mkdir(tempDir, 0755) if err != nil { return fmt.Errorf("error creating temporary directory: %w", err) } @@ -68,3 +100,145 @@ func (f *FileServer) Deploy(ctx context.Context, sourceDir string) error { return nil } + +type fileserverState struct { + contentHash string + configHash string +} + +// downloadAndHashArtifact downloads an artifact and calculates its hash +func downloadAndHashArtifact(ctx context.Context, enclave, artifactName string) (string, error) { + fs, err := ktfs.NewEnclaveFS(ctx, enclave) + if err != nil { + return "", fmt.Errorf("failed to create enclave fs: %w", err) + } + + // Create temp dir + tempDir, err := os.MkdirTemp("", artifactName+"-*") + if err != nil { + return "", fmt.Errorf("failed to create temp dir: %w", err) + } + defer os.RemoveAll(tempDir) + + // Download artifact + artifact, err := fs.GetArtifact(ctx, artifactName) + if err != nil { + return "", fmt.Errorf("failed to get artifact: %w", err) + } + + // Ensure parent directories exist before extracting + if err := os.MkdirAll(tempDir, 0755); err != nil { + return "", fmt.Errorf("failed to create temp dir structure: %w", err) + } + + // Extract to temp dir + if err := artifact.Download(tempDir); err != nil { + return "", fmt.Errorf("failed to download artifact: %w", err) + } + + // Calculate hash + hash, err := calculateDirHash(tempDir) + if err != nil { + return "", fmt.Errorf("failed to calculate hash: %w", err) + } + + return hash, nil +} + +func (f *FileServer) getState(ctx context.Context) <-chan *fileserverState { + stateCh := make(chan *fileserverState) + + go func(ctx context.Context) { + st := &fileserverState{} + var wg sync.WaitGroup + + type artifactInfo struct { + name string + dest *string + } + + artifacts := []artifactInfo{ + {"fileserver-content", &st.contentHash}, + {"fileserver-nginx-conf", &st.configHash}, + } + + for _, art := range artifacts { + wg.Add(1) + go func(art artifactInfo) { + defer wg.Done() + hash, err := downloadAndHashArtifact(ctx, f.enclave, art.name) + if err == nil { + *art.dest = hash + } + }(art) + } + + wg.Wait() + stateCh <- st + }(ctx) + + return stateCh +} + +type entry struct { + RelPath string `json:"rel_path"` + Size int64 `json:"size"` + Mode string `json:"mode"` + Content []byte `json:"content"` +} + +// calculateDirHash returns a SHA256 hash of the directory contents +// It walks through the directory, hashing file names and contents +func calculateDirHash(dir string) (string, error) { + hash := sha256.New() + err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + + // Get path relative to root dir + relPath, err := filepath.Rel(dir, path) + if err != nil { + return err + } + + // Skip the root directory + if relPath == "." { + return nil + } + + // Add the relative path and file info to hash + entry := entry{ + RelPath: relPath, + Size: info.Size(), + Mode: info.Mode().String(), + } + + // If it's a regular file, add its contents to hash + if !info.IsDir() { + content, err := os.ReadFile(path) + if err != nil { + return err + } + entry.Content = content + } + + jsonBytes, err := json.Marshal(entry) + if err != nil { + return err + } + _, err = hash.Write(jsonBytes) + if err != nil { + return err + } + + return nil + }) + + if err != nil { + return "", fmt.Errorf("error walking directory: %w", err) + } + + hashStr := hex.EncodeToString(hash.Sum(nil)) + return hashStr, nil +} diff --git a/kurtosis-devnet/pkg/deploy/fileserver_test.go b/kurtosis-devnet/pkg/deploy/fileserver_test.go index 2b3cd96fadc..ff87eccc485 100644 --- a/kurtosis-devnet/pkg/deploy/fileserver_test.go +++ b/kurtosis-devnet/pkg/deploy/fileserver_test.go @@ -9,6 +9,7 @@ import ( "github.com/ethereum-optimism/optimism/kurtosis-devnet/pkg/kurtosis" "github.com/ethereum-optimism/optimism/kurtosis-devnet/pkg/kurtosis/sources/spec" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -20,36 +21,111 @@ func TestDeployFileserver(t *testing.T) { require.NoError(t, err) defer os.RemoveAll(tmpDir) + // Create test files + sourceDir := filepath.Join(tmpDir, "fileserver") + require.NoError(t, os.MkdirAll(sourceDir, 0755)) + + // Create required directory structure + nginxDir := filepath.Join(sourceDir, "static_files", "nginx") + require.NoError(t, os.MkdirAll(nginxDir, 0755)) + // Create a mock deployer function mockDeployerFunc := func(opts ...kurtosis.KurtosisDeployerOptions) (deployer, error) { return &mockDeployer{}, nil } testCases := []struct { - name string - fs *FileServer - shouldError bool + name string + setup func(t *testing.T, sourceDir, nginxDir string, state *fileserverState) + state *fileserverState + shouldError bool + shouldDeploy bool }{ { - name: "successful deployment", - fs: &FileServer{ - baseDir: tmpDir, - enclave: "test-enclave", - dryRun: true, - deployer: mockDeployerFunc, + name: "empty source directory - no deployment needed", + setup: func(t *testing.T, sourceDir, nginxDir string, state *fileserverState) { + // No files to create }, - shouldError: false, + state: &fileserverState{}, + shouldError: false, + shouldDeploy: false, + }, + { + name: "new files to deploy", + setup: func(t *testing.T, sourceDir, nginxDir string, state *fileserverState) { + require.NoError(t, os.WriteFile( + filepath.Join(sourceDir, "test.txt"), + []byte("test content"), + 0644, + )) + }, + state: &fileserverState{}, + shouldError: false, + shouldDeploy: true, + }, + { + name: "no changes - deployment skipped", + setup: func(t *testing.T, sourceDir, nginxDir string, state *fileserverState) { + require.NoError(t, os.WriteFile( + filepath.Join(sourceDir, "test.txt"), + []byte("test content"), + 0644, + )) + + // Calculate actual hash for the test file + hash, err := calculateDirHash(sourceDir) + require.NoError(t, err) + + // Calculate nginx config hash + configHash, err := calculateDirHash(nginxDir) + require.NoError(t, err) + + // Update state with actual hashes + state.contentHash = hash + state.configHash = configHash + }, + state: &fileserverState{}, + shouldError: false, + shouldDeploy: false, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - err := tc.fs.Deploy(ctx, filepath.Join(tmpDir, "fileserver")) + // Clean up and recreate source directory for each test + require.NoError(t, os.RemoveAll(sourceDir)) + require.NoError(t, os.MkdirAll(sourceDir, 0755)) + + // Recreate nginx directory + require.NoError(t, os.MkdirAll(nginxDir, 0755)) + + // Setup test files + tc.setup(t, sourceDir, nginxDir, tc.state) + + fs := &FileServer{ + baseDir: tmpDir, + enclave: "test-enclave", + dryRun: true, + deployer: mockDeployerFunc, + } + + // Create state channel and send test state + ch := make(chan *fileserverState, 1) + ch <- tc.state + close(ch) + + err := fs.Deploy(ctx, sourceDir, ch) if tc.shouldError { require.Error(t, err) } else { require.NoError(t, err) } + + // Verify deployment directory was created only if deployment was needed + deployDir := filepath.Join(tmpDir, FILESERVER_PACKAGE) + if tc.shouldDeploy { + assert.DirExists(t, deployDir) + } }) } } diff --git a/kurtosis-devnet/tests/fees/fees_test.go b/kurtosis-devnet/tests/fees/fees_test.go new file mode 100644 index 00000000000..fc24502069f --- /dev/null +++ b/kurtosis-devnet/tests/fees/fees_test.go @@ -0,0 +1,383 @@ +package fees + +import ( + "context" + "errors" + "math/big" + "testing" + "time" + + "github.com/ethereum-optimism/optimism/devnet-sdk/system" + "github.com/ethereum-optimism/optimism/devnet-sdk/testing/systest" + "github.com/ethereum-optimism/optimism/devnet-sdk/testing/testlib/validators" + "github.com/ethereum-optimism/optimism/devnet-sdk/types" + "github.com/ethereum-optimism/optimism/op-e2e/bindings" + "github.com/ethereum-optimism/optimism/op-node/rollup" + "github.com/ethereum-optimism/optimism/op-service/predeploys" + "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + gethTypes "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/ethclient" + "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/rpc" + "github.com/stretchr/testify/require" +) + +// TestFees verifies that L1/L2 fees are handled properly in different fork configurations +func TestFees(t *testing.T) { + // Define which L2 chain we'll test + chainIdx := uint64(0) + + // Get validators and getters for accessing the system and wallets + lowLevelSystemGetter, lowLevelSystemValidator := validators.AcquireLowLevelSystem() + walletGetter, walletValidator := validators.AcquireL2WalletWithFunds(chainIdx, types.NewBalance(big.NewInt(params.Ether))) + + // Run pre-regolith test + forkGetter, forkValidator := validators.AcquireL2WithoutFork(chainIdx, rollup.Regolith) + systest.SystemTest(t, + feesTestScenario(lowLevelSystemGetter, walletGetter, chainIdx, forkGetter), + lowLevelSystemValidator, + walletValidator, + forkValidator, + ) + + // Run regolith test + forkGetter, forkValidator = validators.AcquireL2WithFork(chainIdx, rollup.Regolith) + _, notForkValidator := validators.AcquireL2WithoutFork(chainIdx, rollup.Ecotone) + systest.SystemTest(t, + feesTestScenario(lowLevelSystemGetter, walletGetter, chainIdx, forkGetter), + lowLevelSystemValidator, + walletValidator, + forkValidator, + notForkValidator, + ) + + // Run ecotone test + forkGetter, forkValidator = validators.AcquireL2WithFork(chainIdx, rollup.Ecotone) + _, notForkValidator = validators.AcquireL2WithoutFork(chainIdx, rollup.Fjord) + systest.SystemTest(t, + feesTestScenario(lowLevelSystemGetter, walletGetter, chainIdx, forkGetter), + lowLevelSystemValidator, + walletValidator, + forkValidator, + notForkValidator, + ) + + // Run fjord test + forkGetter, forkValidator = validators.AcquireL2WithFork(chainIdx, rollup.Fjord) + _, notForkValidator = validators.AcquireL2WithoutFork(chainIdx, rollup.Isthmus) + systest.SystemTest(t, + feesTestScenario(lowLevelSystemGetter, walletGetter, chainIdx, forkGetter), + lowLevelSystemValidator, + walletValidator, + forkValidator, + notForkValidator, + ) + + // Run isthmus test + forkGetter, forkValidator = validators.AcquireL2WithFork(chainIdx, rollup.Isthmus) + systest.SystemTest(t, + feesTestScenario(lowLevelSystemGetter, walletGetter, chainIdx, forkGetter), + lowLevelSystemValidator, + walletValidator, + forkValidator, + ) +} + +// stateGetterAdapter adapts the ethclient to implement the StateGetter interface +type stateGetterAdapter struct { + ctx context.Context + t systest.T + client *ethclient.Client +} + +// GetState implements the StateGetter interface +func (sga *stateGetterAdapter) GetState(addr common.Address, key common.Hash) common.Hash { + var result common.Hash + val, err := sga.client.StorageAt(sga.ctx, addr, key, nil) + require.NoError(sga.t, err) + copy(result[:], val) + return result +} + +// waitForTransaction polls for a transaction receipt until it is available or the context is canceled. +// It's a simpler version of the functionality in SimpleTxManager. +func waitForTransaction(ctx context.Context, client *ethclient.Client, hash common.Hash) (*gethTypes.Receipt, error) { + ticker := time.NewTicker(500 * time.Millisecond) // Poll every 500ms + defer ticker.Stop() + + for { + receipt, err := client.TransactionReceipt(ctx, hash) + if receipt != nil && err == nil { + return receipt, nil + } else if err != nil && !errors.Is(err, ethereum.NotFound) { + return nil, err + } + + select { + case <-ctx.Done(): + return nil, ctx.Err() + case <-ticker.C: + // Continue polling + } + } +} + +// feesTestScenario creates a test scenario for verifying fee calculations +func feesTestScenario( + lowLevelSystemGetter validators.LowLevelSystemGetter, + walletGetter validators.WalletGetter, + chainIdx uint64, + configGetter validators.ChainConfigGetter, +) systest.SystemTestFunc { + return func(t systest.T, sys system.System) { + ctx := t.Context() + + // Get the low-level system and wallet + llsys := lowLevelSystemGetter(ctx) + wallet := walletGetter(ctx) + config := configGetter(ctx) + + // Get the L2 client + l2Chain := llsys.L2s()[chainIdx] + l2Client, err := l2Chain.Client() + require.NoError(t, err) + + // Get the L1 client + l1Chain := llsys.L1() + l1Client, err := l1Chain.Client() + require.NoError(t, err) + + // TODO: Wait for first block after genesis + // The genesis block has zero L1Block values and will throw off the GPO checks + _, err = l2Client.HeaderByNumber(ctx, big.NewInt(1)) + require.NoError(t, err) + + // Get the genesis config + chainConfig, err := l2Chain.Config() + require.NoError(t, err) + + // Create state getter adapter for L1 cost function + sga := &stateGetterAdapter{ + ctx: ctx, + t: t, + client: l2Client, + } + + // Create L1 cost function + l1CostFn := gethTypes.NewL1CostFunc(chainConfig, sga) + + // Create operator fee function + operatorFeeFn := gethTypes.NewOperatorCostFunc(chainConfig, sga) + + // Get wallet private key and address + fromAddr := wallet.Address() + privateKey := wallet.PrivateKey() + + // Find gaspriceoracle contract + gpoContract, err := bindings.NewGasPriceOracle(predeploys.GasPriceOracleAddr, l2Client) + require.NoError(t, err) + + // Get wallet balance before test + startBalance, err := l2Client.BalanceAt(ctx, fromAddr, big.NewInt(rpc.EarliestBlockNumber.Int64())) + require.NoError(t, err) + require.Greater(t, startBalance.Uint64(), big.NewInt(0).Uint64()) + + // Get initial balances of fee recipients + baseFeeRecipientStartBalance, err := l2Client.BalanceAt(ctx, predeploys.BaseFeeVaultAddr, big.NewInt(rpc.EarliestBlockNumber.Int64())) + require.NoError(t, err) + + l1FeeRecipientStartBalance, err := l2Client.BalanceAt(ctx, predeploys.L1FeeVaultAddr, big.NewInt(rpc.EarliestBlockNumber.Int64())) + require.NoError(t, err) + + sequencerFeeVaultStartBalance, err := l2Client.BalanceAt(ctx, predeploys.SequencerFeeVaultAddr, big.NewInt(rpc.EarliestBlockNumber.Int64())) + require.NoError(t, err) + + operatorFeeVaultStartBalance, err := l2Client.BalanceAt(ctx, predeploys.OperatorFeeVaultAddr, big.NewInt(rpc.EarliestBlockNumber.Int64())) + require.NoError(t, err) + + genesisBlock, err := l2Client.BlockByNumber(ctx, big.NewInt(rpc.EarliestBlockNumber.Int64())) + require.NoError(t, err) + + coinbaseStartBalance, err := l2Client.BalanceAt(ctx, genesisBlock.Coinbase(), big.NewInt(rpc.EarliestBlockNumber.Int64())) + require.NoError(t, err) + + // Send a simple transfer from wallet to a test address + transferAmount := big.NewInt(params.Ether / 10) // 0.1 ETH + targetAddr := common.Address{0xff, 0xff} + + // Get suggested gas tip from the client instead of using a hardcoded value + gasTip, err := l2Client.SuggestGasTipCap(ctx) + require.NoError(t, err, "Failed to get suggested gas tip") + + // Estimate gas for the transaction instead of using a hardcoded value + msg := ethereum.CallMsg{ + From: fromAddr, + To: &targetAddr, + Value: transferAmount, + } + gasLimit, err := l2Client.EstimateGas(ctx, msg) + require.NoError(t, err, "Failed to estimate gas") + + // Create and sign transaction with the suggested values + nonce, err := l2Client.PendingNonceAt(ctx, fromAddr) + require.NoError(t, err) + + // Get latest header to get the base fee + header, err := l2Client.HeaderByNumber(ctx, nil) + require.NoError(t, err) + + // Calculate a reasonable gas fee cap based on the base fee + // A common approach is to set fee cap to 2x the base fee + tip + gasFeeCap := new(big.Int).Add( + new(big.Int).Mul(header.BaseFee, big.NewInt(2)), + gasTip, + ) + + txData := &gethTypes.DynamicFeeTx{ + ChainID: l2Chain.ID(), + Nonce: nonce, + GasTipCap: gasTip, + GasFeeCap: gasFeeCap, + Gas: gasLimit, + To: &targetAddr, + Value: transferAmount, + Data: nil, + } + + // Sign transaction + tx := gethTypes.NewTx(txData) + signedTx, err := gethTypes.SignTx(tx, gethTypes.LatestSignerForChainID(l2Chain.ID()), privateKey) + require.NoError(t, err) + + // Send transaction + err = l2Client.SendTransaction(ctx, signedTx) + require.NoError(t, err) + + // Wait for transaction receipt with timeout + ctx, cancel := context.WithTimeout(ctx, time.Second*10) + defer cancel() + receipt, err := waitForTransaction(ctx, l2Client, signedTx.Hash()) + require.NoError(t, err, "Failed to wait for transaction receipt") + require.NotNil(t, receipt) + require.Equal(t, gethTypes.ReceiptStatusSuccessful, receipt.Status) + + // Get block header where transaction was included + header, err = l2Client.HeaderByNumber(ctx, receipt.BlockNumber) + require.NoError(t, err) + + // Get final balances after transaction + coinbaseEndBalance, err := l2Client.BalanceAt(ctx, header.Coinbase, header.Number) + require.NoError(t, err) + + endBalance, err := l2Client.BalanceAt(ctx, fromAddr, header.Number) + require.NoError(t, err) + + baseFeeRecipientEndBalance, err := l2Client.BalanceAt(ctx, predeploys.BaseFeeVaultAddr, header.Number) + require.NoError(t, err) + + operatorFeeVaultEndBalance, err := l2Client.BalanceAt(ctx, predeploys.OperatorFeeVaultAddr, header.Number) + require.NoError(t, err) + + l1Header, err := l1Client.HeaderByNumber(ctx, nil) + require.NoError(t, err) + + l1FeeRecipientEndBalance, err := l2Client.BalanceAt(ctx, predeploys.L1FeeVaultAddr, header.Number) + require.NoError(t, err) + + sequencerFeeVaultEndBalance, err := l2Client.BalanceAt(ctx, predeploys.SequencerFeeVaultAddr, header.Number) + require.NoError(t, err) + + // Calculate differences in balances + baseFeeRecipientDiff := new(big.Int).Sub(baseFeeRecipientEndBalance, baseFeeRecipientStartBalance) + l1FeeRecipientDiff := new(big.Int).Sub(l1FeeRecipientEndBalance, l1FeeRecipientStartBalance) + sequencerFeeVaultDiff := new(big.Int).Sub(sequencerFeeVaultEndBalance, sequencerFeeVaultStartBalance) + coinbaseDiff := new(big.Int).Sub(coinbaseEndBalance, coinbaseStartBalance) + operatorFeeVaultDiff := new(big.Int).Sub(operatorFeeVaultEndBalance, operatorFeeVaultStartBalance) + + // Verify L2 fee + l2Fee := new(big.Int).Mul(gasTip, new(big.Int).SetUint64(receipt.GasUsed)) + require.Equal(t, sequencerFeeVaultDiff, coinbaseDiff, "coinbase is always sequencer fee vault") + require.Equal(t, l2Fee, coinbaseDiff, "l2 fee mismatch") + require.Equal(t, l2Fee, sequencerFeeVaultDiff) + + // Verify base fee + baseFee := new(big.Int).Mul(header.BaseFee, new(big.Int).SetUint64(receipt.GasUsed)) + require.Equal(t, baseFee, baseFeeRecipientDiff, "base fee mismatch") + + // Verify L1 fee + txBytes, err := signedTx.MarshalBinary() + require.NoError(t, err) + + // Calculate L1 fee based on transaction data and blocktime + l1Fee := l1CostFn(tx.RollupCostData(), header.Time) + require.Equal(t, l1Fee, l1FeeRecipientDiff, "L1 fee mismatch") + + // Calculate operator fee + expectedOperatorFee := operatorFeeFn(receipt.GasUsed, header.Time) + expectedOperatorFeeVaultEndBalance := new(big.Int).Sub(operatorFeeVaultStartBalance, expectedOperatorFee.ToBig()) + require.True(t, + operatorFeeVaultDiff.Cmp(expectedOperatorFee.ToBig()) == 0, + "operator fee mismatch: operator fee vault start balance %v, actual end balance %v, expected end balance %v", + operatorFeeVaultStartBalance, + operatorFeeVaultEndBalance, + expectedOperatorFeeVaultEndBalance, + ) + + // Verify GPO matches expected state + gpoEcotone, err := gpoContract.IsEcotone(nil) + require.NoError(t, err) + + block, err := l2Chain.Node().BlockByNumber(t.Context(), nil) + require.NoError(t, err) + time := block.Header().Time + + ecotoneEnabled, err := validators.IsForkActivated(config, rollup.Ecotone, time) + require.NoError(t, err) + require.Equal(t, ecotoneEnabled, gpoEcotone, "GPO and chain must have same ecotone view") + + gpoFjord, err := gpoContract.IsFjord(nil) + require.NoError(t, err) + fjordEnabled, err := validators.IsForkActivated(config, rollup.Fjord, time) + require.NoError(t, err) + require.Equal(t, fjordEnabled, gpoFjord, "GPO and chain must have same fjord view") + + gpoIsthmus, err := gpoContract.IsIsthmus(nil) + require.NoError(t, err) + isthmusEnabled, err := validators.IsForkActivated(config, rollup.Isthmus, time) + require.NoError(t, err) + require.Equal(t, isthmusEnabled, gpoIsthmus, "GPO and chain must have same isthmus view") + + // Verify gas price oracle L1 fee calculation + gpoL1Fee, err := gpoContract.GetL1Fee(&bind.CallOpts{}, txBytes) + require.NoError(t, err) + + regolithEnabled, err := validators.IsForkActivated(config, rollup.Regolith, time) + require.NoError(t, err) + + adjustedGPOFee := gpoL1Fee + if fjordEnabled { + // The fjord calculations are handled differently and may be bounded by the minimum value + // This is a simplification as the full test adapts more precisely + } else if regolithEnabled && !ecotoneEnabled { + // For post-regolith (but pre-ecotone), adjust the GPO fee to account for signature overhead + artificialGPOOverhead := big.NewInt(68 * 16) // 68 bytes to cover signature and RLP data + l1BaseFee := l1Header.BaseFee + adjustedGPOFee = new(big.Int).Sub(gpoL1Fee, new(big.Int).Mul(artificialGPOOverhead, l1BaseFee)) + } + require.Equal(t, l1Fee, adjustedGPOFee, "GPO reports L1 fee mismatch") + + // Verify receipt L1 fee + require.Equal(t, receipt.L1Fee, l1Fee, "l1 fee in receipt is correct") + + // Calculate total fee and verify wallet balance difference + totalFeeRecipient := new(big.Int).Add(baseFeeRecipientDiff, sequencerFeeVaultDiff) + totalFee := new(big.Int).Add(totalFeeRecipient, l1FeeRecipientDiff) + totalFee = new(big.Int).Add(totalFee, operatorFeeVaultDiff) + + balanceDiff := new(big.Int).Sub(startBalance, endBalance) + balanceDiff.Sub(balanceDiff, transferAmount) + require.Equal(t, balanceDiff, totalFee, "balances should add up") + } +} diff --git a/kurtosis-devnet/tests/fjord/check_scripts_test.go b/kurtosis-devnet/tests/fjord/check_scripts_test.go new file mode 100644 index 00000000000..e3284241bf6 --- /dev/null +++ b/kurtosis-devnet/tests/fjord/check_scripts_test.go @@ -0,0 +1,93 @@ +package fjord + +import ( + "math/big" + "testing" + + "github.com/ethereum-optimism/optimism/devnet-sdk/system" + "github.com/ethereum-optimism/optimism/devnet-sdk/testing/systest" + "github.com/ethereum-optimism/optimism/devnet-sdk/testing/testlib/validators" + "github.com/ethereum-optimism/optimism/devnet-sdk/types" + fjordChecks "github.com/ethereum-optimism/optimism/op-chain-ops/cmd/check-fjord/checks" + "github.com/ethereum-optimism/optimism/op-node/rollup" + "github.com/ethereum-optimism/optimism/op-service/testlog" + "github.com/ethereum/go-ethereum/log" + "github.com/stretchr/testify/require" +) + +// TestCheckFjordScript ensures the op-chain-ops/cmd/check-fjord script runs successfully +// against a test chain with the fjord hardfork activated/unactivated +func TestCheckFjordScript(t *testing.T) { + + l2ChainIndex := uint64(0) + + lowLevelSystemGetter, lowLevelSystemValidator := validators.AcquireLowLevelSystem() + walletGetter, walletValidator := validators.AcquireL2WalletWithFunds(l2ChainIndex, types.NewBalance(big.NewInt(1_000_000))) + forkConfigGetter, forkValidatorA := validators.AcquireL2WithFork(l2ChainIndex, rollup.Fjord) + _, forkValidatorB := validators.AcquireL2WithoutFork(l2ChainIndex, rollup.Granite) + systest.SystemTest(t, + checkFjordScriptScenario(lowLevelSystemGetter, walletGetter, forkConfigGetter, l2ChainIndex), + lowLevelSystemValidator, + walletValidator, + forkValidatorA, + forkValidatorB, + ) + + forkConfigGetter, notForkValidator := validators.AcquireL2WithoutFork(l2ChainIndex, rollup.Fjord) + systest.SystemTest(t, + checkFjordScriptScenario(lowLevelSystemGetter, walletGetter, forkConfigGetter, l2ChainIndex), + lowLevelSystemValidator, + walletValidator, + notForkValidator, + ) + +} + +func checkFjordScriptScenario(lowLevelSystemGetter validators.LowLevelSystemGetter, walletGetter validators.WalletGetter, chainConfigGetter validators.ChainConfigGetter, chainIndex uint64) systest.SystemTestFunc { + return func(t systest.T, sys system.System) { + llsys := lowLevelSystemGetter(t.Context()) + wallet := walletGetter(t.Context()) + chainConfig := chainConfigGetter(t.Context()) + + l2 := sys.L2s()[chainIndex] + l2LowLevelClient, err := llsys.L2s()[chainIndex].Client() + require.NoError(t, err) + + // Get the wallet's private key and address + privateKey := wallet.PrivateKey() + walletAddr := wallet.Address() + + logger := testlog.Logger(t, log.LevelDebug) + checkFjordConfig := &fjordChecks.CheckFjordConfig{ + Log: logger, + L2: l2LowLevelClient, + Key: privateKey, + Addr: walletAddr, + } + + block, err := l2.Node().BlockByNumber(t.Context(), nil) + require.NoError(t, err) + time := block.Header().Time + + isFjordActivated, err := validators.IsForkActivated(chainConfig, rollup.Fjord, time) + require.NoError(t, err) + + if !isFjordActivated { + err = fjordChecks.CheckRIP7212(t.Context(), checkFjordConfig) + require.Error(t, err, "expected error for CheckRIP7212") + err = fjordChecks.CheckGasPriceOracle(t.Context(), checkFjordConfig) + require.Error(t, err, "expected error for CheckGasPriceOracle") + err = fjordChecks.CheckTxEmpty(t.Context(), checkFjordConfig) + require.Error(t, err, "expected error for CheckTxEmpty") + err = fjordChecks.CheckTxAllZero(t.Context(), checkFjordConfig) + require.Error(t, err, "expected error for CheckTxAllZero") + err = fjordChecks.CheckTxAll42(t.Context(), checkFjordConfig) + require.Error(t, err, "expected error for CheckTxAll42") + err = fjordChecks.CheckTxRandom(t.Context(), checkFjordConfig) + require.Error(t, err, "expected error for CheckTxRandom") + } else { + err = fjordChecks.CheckAll(t.Context(), checkFjordConfig) + require.NoError(t, err, "should not error on CheckAll") + } + } +} diff --git a/kurtosis-devnet/tests/isthmus/erc20_bridge_test.go b/kurtosis-devnet/tests/isthmus/erc20_bridge_test.go new file mode 100644 index 00000000000..6014ab03d05 --- /dev/null +++ b/kurtosis-devnet/tests/isthmus/erc20_bridge_test.go @@ -0,0 +1,213 @@ +package isthmus + +import ( + "fmt" + "math/big" + "testing" + "time" + + "github.com/stretchr/testify/require" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/params" + + "github.com/ethereum-optimism/optimism/devnet-sdk/contracts/constants" + "github.com/ethereum-optimism/optimism/devnet-sdk/system" + "github.com/ethereum-optimism/optimism/devnet-sdk/testing/systest" + "github.com/ethereum-optimism/optimism/devnet-sdk/testing/testlib/validators" + sdktypes "github.com/ethereum-optimism/optimism/devnet-sdk/types" + "github.com/ethereum-optimism/optimism/op-e2e/bindings" + "github.com/ethereum-optimism/optimism/op-e2e/e2eutils/wait" + "github.com/ethereum-optimism/optimism/op-service/predeploys" + "github.com/ethereum-optimism/optimism/op-service/testlog" +) + +func TestERC20Bridge(t *testing.T) { + chainIdx := uint64(0) // We'll use the first L2 chain for this test + + l2WalletGetter, l2WalletFundsValidator := validators.AcquireL2WalletWithFunds( + chainIdx, + sdktypes.NewBalance(big.NewInt(1.0*constants.ETH)), + ) + l1WalletGetter, l1WalletFundsValidator := validators.AcquireL1WalletWithFunds(sdktypes.NewBalance(big.NewInt(1.0 * constants.ETH))) + lowLevelSystemGetter, lowLevelSystemValidator := validators.AcquireLowLevelSystem() + + systest.SystemTest(t, + erc20BridgeTestScenario(lowLevelSystemGetter, chainIdx, l1WalletGetter, l2WalletGetter), + l2WalletFundsValidator, + l1WalletFundsValidator, + lowLevelSystemValidator, + ) +} + +// erc20BridgeTestScenario tests depositing an ERC20 token from L1 to L2 through the bridge +func erc20BridgeTestScenario(lowLevelSystemGetter validators.LowLevelSystemGetter, chainIdx uint64, l1WalletGetter validators.WalletGetter, l2WalletGetter validators.WalletGetter) systest.SystemTestFunc { + return func(t systest.T, sys system.System) { + ctx := t.Context() + + // Get the l2User wallet + llsys := lowLevelSystemGetter(ctx) + l1User := l1WalletGetter(ctx) + l2User := l2WalletGetter(ctx) + + // Get the L1 chain + l1Chain := llsys.L1() + + // Get the L2 chain + l2Chain := llsys.L2s()[chainIdx] + + logger := testlog.Logger(t, log.LevelInfo) + logger.Info("Started ERC20 bridge test") + + // Connect to L1 and L2 + l1Client, err := l1Chain.Client() + require.NoError(t, err) + t.Cleanup(func() { l1Client.Close() }) + + l2Client, err := l2Chain.Client() + require.NoError(t, err) + t.Cleanup(func() { l2Client.Close() }) + + // Print the L1 chain ID as a string + logger.Info("L1 Chain ID", "id", l1Chain.ID()) + + // Create transaction options for L1 + l1Opts, err := bind.NewKeyedTransactorWithChainID(l1User.PrivateKey(), l1Chain.ID()) + require.NoError(t, err) + + // Create transaction options for L2 + l2Opts, err := bind.NewKeyedTransactorWithChainID(l2User.PrivateKey(), l2Chain.ID()) + require.NoError(t, err) + + // Deploy a test ERC20 token on L1 (WETH) + logger.Info("Deploying WETH token on L1") + l1TokenAddress, tx, l1Token, err := bindings.DeployWETH(l1Opts, l1Client) + require.NoError(t, err) + + // Wait for the token deployment transaction to be confirmed + _, err = wait.ForReceiptOK(ctx, l1Client, tx.Hash()) + require.NoError(t, err, "Failed to deploy L1 token") + logger.Info("Deployed L1 token", "address", l1TokenAddress) + + // Mint some tokens to the user (deposit ETH to get WETH) + mintAmount := big.NewInt(params.Ether) // 1 ETH + l1Opts.Value = mintAmount + tx, err = l1Token.Deposit(l1Opts) + require.NoError(t, err) + _, err = wait.ForReceiptOK(ctx, l1Client, tx.Hash()) + require.NoError(t, err, "Failed to mint L1 tokens") + l1Opts.Value = nil + + l1Balance, err := l1Token.BalanceOf(&bind.CallOpts{}, l1User.Address()) + require.NoError(t, err) + require.Equal(t, mintAmount, l1Balance, "User should have the minted tokens on L1") + logger.Info("User has tokens on L1", "balance", l1Balance) + + // Create the corresponding L2 token using the OptimismMintableERC20Factory + logger.Info("Creating L2 token via OptimismMintableERC20Factory") + optimismMintableTokenFactory, err := bindings.NewOptimismMintableERC20Factory(predeploys.OptimismMintableERC20FactoryAddr, l2Client) + require.NoError(t, err) + + // Create the L2 token + l2TokenName := "L2 Test Token" + l2TokenSymbol := "L2TEST" + tx, err = optimismMintableTokenFactory.CreateOptimismMintableERC20(l2Opts, l1TokenAddress, l2TokenName, l2TokenSymbol) + require.NoError(t, err) + l2TokenReceipt, err := wait.ForReceiptOK(ctx, l2Client, tx.Hash()) + require.NoError(t, err, "Failed to create L2 token") + + // Extract the L2 token address from the event logs + var l2TokenAddress common.Address + for _, log := range l2TokenReceipt.Logs { + createdEvent, err := optimismMintableTokenFactory.ParseOptimismMintableERC20Created(*log) + if err == nil && createdEvent != nil { + l2TokenAddress = createdEvent.LocalToken + break + } + } + require.NotEqual(t, common.Address{}, l2TokenAddress, "Failed to find L2 token address from events") + logger.Info("Created L2 token", "address", l2TokenAddress) + + // Get the L2 token contract + l2Token, err := bindings.NewOptimismMintableERC20(l2TokenAddress, l2Client) + require.NoError(t, err) + + // Check initial L2 token balance (should be 0) + initialL2Balance, err := l2Token.BalanceOf(&bind.CallOpts{}, l2User.Address()) + require.NoError(t, err) + require.True(t, big.NewInt(0).Cmp(initialL2Balance) == 0, "Initial L2 token balance should be 0, actual was %s", initialL2Balance.String()) + + l1StandardBridgeAddress, ok := l2Chain.Addresses()["l1StandardBridgeProxy"] + require.True(t, ok, fmt.Errorf("no L1 proxy address configured for this test")) + + l1StandardBridge, err := bindings.NewL1StandardBridge(l1StandardBridgeAddress, l1Client) + require.NoError(t, err) + + // Approve the L1 bridge to spend tokens + logger.Info("Approving L1 bridge to spend tokens") + tx, err = l1Token.Approve(l1Opts, l1StandardBridgeAddress, mintAmount) + require.NoError(t, err) + _, err = wait.ForReceiptOK(ctx, l1Client, tx.Hash()) + require.NoError(t, err, "Failed to approve L1 bridge") + + // Amount to bridge + bridgeAmount := big.NewInt(params.Ether / 10) // 0.1 token + minGasLimit := uint32(200000) // Minimum gas limit for the L2 transaction + + // Bridge the tokens from L1 to L2 + logger.Info("Bridging tokens from L1 to L2", "amount", bridgeAmount) + tx, err = l1StandardBridge.DepositERC20To( + l1Opts, + l1TokenAddress, + l2TokenAddress, + l2User.Address(), + bridgeAmount, + minGasLimit, + []byte{}, // No extra data + ) + require.NoError(t, err) + depositReceipt, err := wait.ForReceiptOK(ctx, l1Client, tx.Hash()) + require.NoError(t, err, "Failed to deposit tokens to L2") + logger.Info("Deposit transaction confirmed on L1", "tx", tx.Hash().Hex()) + + // Get the OptimismPortal contract to find the deposit event + optimismPortal, err := bindings.NewOptimismPortal(l2Chain.Addresses()["optimismPortalProxy"], l1Client) + require.NoError(t, err) + + // Find the TransactionDeposited event from the logs + var depositFound bool + for _, log := range depositReceipt.Logs { + depositEvent, err := optimismPortal.ParseTransactionDeposited(*log) + if err == nil && depositEvent != nil { + logger.Info("Found deposit event", "from", depositEvent.From) + depositFound = true + break + } + } + require.True(t, depositFound, "No deposit event found in transaction logs") + + // Wait for the deposit to be processed on L2 + // This may take some time as it depends on the L2 block time and the deposit processing + logger.Info("Waiting for deposit to be processed on L2...") + + // Poll for the L2 balance to change + err = wait.For(ctx, 200*time.Millisecond, func() (bool, error) { + l2Balance, err := l2Token.BalanceOf(&bind.CallOpts{}, l2User.Address()) + if err != nil { + return false, err + } + return l2Balance.Cmp(initialL2Balance) > 0, nil + }) + require.NoError(t, err, "Timed out waiting for L2 balance to change") + + // Verify the final L2 balance + finalL2Balance, err := l2Token.BalanceOf(&bind.CallOpts{}, l2User.Address()) + require.NoError(t, err) + require.True(t, bridgeAmount.Cmp(finalL2Balance) == 0, "L2 balance should match the bridged amount, L2 balance=%s, bridged amount=%s", finalL2Balance, bridgeAmount) + logger.Info("Successfully verified tokens on L2", "balance", finalL2Balance) + + logger.Info("ERC20 bridge test completed successfully!") + } +} diff --git a/kurtosis-devnet/tests/isthmus/withdrawal_root_test.go b/kurtosis-devnet/tests/isthmus/withdrawal_root_test.go new file mode 100644 index 00000000000..1f39ee39d71 --- /dev/null +++ b/kurtosis-devnet/tests/isthmus/withdrawal_root_test.go @@ -0,0 +1,154 @@ +package isthmus + +import ( + "math/big" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/common" + gtypes "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/log" + + "github.com/ethereum-optimism/optimism/devnet-sdk/contracts/constants" + "github.com/ethereum-optimism/optimism/devnet-sdk/system" + "github.com/ethereum-optimism/optimism/devnet-sdk/testing/systest" + "github.com/ethereum-optimism/optimism/devnet-sdk/testing/testlib/validators" + "github.com/ethereum-optimism/optimism/devnet-sdk/types" + "github.com/ethereum-optimism/optimism/op-e2e/e2eutils/wait" + "github.com/ethereum-optimism/optimism/op-node/rollup" + "github.com/ethereum-optimism/optimism/op-service/client" + "github.com/ethereum-optimism/optimism/op-service/predeploys" + "github.com/ethereum-optimism/optimism/op-service/sources" + "github.com/ethereum-optimism/optimism/op-service/testlog" + "github.com/lmittmann/w3" +) + +func TestWithdrawalsRoot(t *testing.T) { + chainIdx := uint64(0) // We'll use the first L2 chain for this test + + walletGetter, fundsValidator := validators.AcquireL2WalletWithFunds( + chainIdx, + types.NewBalance(big.NewInt(1.0*constants.ETH)), + ) + llsysGetter, llsysValidator := validators.AcquireLowLevelSystem() + _, forkValidator := validators.AcquireL2WithFork(chainIdx, rollup.Isthmus) + + systest.SystemTest(t, + withdrawalRootTestScenario(chainIdx, walletGetter, llsysGetter), + fundsValidator, + llsysValidator, + forkValidator, + ) +} + +func withdrawalRootTestScenario(chainIdx uint64, walletGetter validators.WalletGetter, llsysGetter validators.LowLevelSystemGetter) systest.SystemTestFunc { + return func(t systest.T, sys system.System) { + ctx := t.Context() + + llsys := llsysGetter(ctx) + chain := llsys.L2s()[chainIdx] + gethCl, err := chain.Client() + require.NoError(t, err) + + logger := testlog.Logger(t, log.LevelInfo) + logger.Info("Started test") + + user := walletGetter(ctx) + + // Sad eth clients + rpcCl, err := client.NewRPC(ctx, logger, chain.RPCURL()) + require.NoError(t, err) + t.Cleanup(rpcCl.Close) + ethCl, err := sources.NewEthClient(rpcCl, logger, nil, sources.DefaultEthClientConfig(10)) + require.NoError(t, err) + + // Determine pre-state + preBlock, err := gethCl.BlockByNumber(ctx, nil) + require.NoError(t, err) + logger.Info("Got pre-state block", "hash", preBlock.Hash(), "number", preBlock.Number()) + + preBlockHash := preBlock.Hash() + preProof, err := ethCl.GetProof(ctx, predeploys.L2ToL1MessagePasserAddr, nil, preBlockHash.String()) + require.NoError(t, err) + preWithdrawalsRoot := preProof.StorageHash + + logger.Info("Got pre proof", "storage hash", preWithdrawalsRoot) + + // check isthmus withdrawals-root in the block matches the state + gotPre := preBlock.WithdrawalsRoot() + require.NotNil(t, gotPre) + require.Equal(t, preWithdrawalsRoot, *gotPre, "withdrawals root in block is what we expect") + + chainID := (*big.Int)(chain.ID()) + signer := gtypes.LatestSignerForChainID(chainID) + priv := user.PrivateKey() + require.NoError(t, err) + + // construct call input, ugly but no bindings... + funcInitiateWithdrawal := w3.MustNewFunc(`initiateWithdrawal(address, uint256, bytes memory)`, "") + args, err := funcInitiateWithdrawal.EncodeArgs( + common.Address{}, + big.NewInt(1_000_000), + []byte{}, + ) + require.NoError(t, err) + + // Try to simulate the transaction first to check for errors + gasLimit, err := gethCl.EstimateGas(ctx, ethereum.CallMsg{ + From: user.Address(), + To: &predeploys.L2ToL1MessagePasserAddr, + Value: big.NewInt(0), + Data: args, + }) + require.NoError(t, err, "Gas estimation failed") + + nonce, err := gethCl.PendingNonceAt(ctx, user.Address()) + require.NoError(t, err) + + gasPrice, err := gethCl.SuggestGasPrice(ctx) + require.NoError(t, err, "failed to suggest gas price") + + tip, err := gethCl.SuggestGasTipCap(ctx) + require.NoError(t, err, "error getting gas tip cap") + + tx, err := gtypes.SignNewTx(priv, signer, >ypes.DynamicFeeTx{ + ChainID: chainID, + Nonce: nonce, + GasTipCap: tip, + GasFeeCap: new(big.Int).Add(tip, new(big.Int).Mul(gasPrice, big.NewInt(2))), + Gas: gasLimit, + To: &predeploys.L2ToL1MessagePasserAddr, + Value: big.NewInt(0), + Data: args, + }) + require.NoError(t, err, "sign tx") + + err = gethCl.SendTransaction(ctx, tx) + require.NoError(t, err, "send tx") + + // Find when the withdrawal waskincluded + rec, err := wait.ForReceipt(ctx, gethCl, tx.Hash(), gtypes.ReceiptStatusSuccessful) + require.NoError(t, err) + + // Load the storage at this particular block + postBlockHash := rec.BlockHash + postProof, err := ethCl.GetProof(ctx, predeploys.L2ToL1MessagePasserAddr, nil, postBlockHash.String()) + require.NoError(t, err, "Error getting L2ToL1MessagePasser contract proof") + postWithdrawalsRoot := postProof.StorageHash + + // Check that the withdrawals-root changed + require.NotEqual(t, preWithdrawalsRoot, postWithdrawalsRoot, "withdrawals storage root changes") + + postBlock, err := gethCl.BlockByHash(ctx, postBlockHash) + require.NoError(t, err) + logger.Info("Got post-state block", "hash", postBlock.Hash(), "number", postBlock.Number()) + + gotPost := postBlock.WithdrawalsRoot() + require.NotNil(t, gotPost) + require.Equal(t, postWithdrawalsRoot, *gotPost, "block contains new withdrawals root") + + logger.Info("Withdrawals root test passed") + } +} diff --git a/op-chain-ops/interopgen/configs.go b/op-chain-ops/interopgen/configs.go index a365fd6cb78..c3113d240e1 100644 --- a/op-chain-ops/interopgen/configs.go +++ b/op-chain-ops/interopgen/configs.go @@ -74,16 +74,15 @@ type L2Config struct { Challenger common.Address SystemConfigOwner common.Address genesis.L2InitializationConfig - Prefund map[common.Address]*big.Int - SaltMixer string - GasLimit uint64 - DisputeGameUsesSuperRoots bool - DisputeGameType uint32 - DisputeAbsolutePrestate common.Hash - DisputeMaxGameDepth uint64 - DisputeSplitDepth uint64 - DisputeClockExtension uint64 - DisputeMaxClockDuration uint64 + Prefund map[common.Address]*big.Int + SaltMixer string + GasLimit uint64 + DisputeGameType uint32 + DisputeAbsolutePrestate common.Hash + DisputeMaxGameDepth uint64 + DisputeSplitDepth uint64 + DisputeClockExtension uint64 + DisputeMaxClockDuration uint64 } func (c *L2Config) Check(log log.Logger) error { diff --git a/op-chain-ops/interopgen/deploy.go b/op-chain-ops/interopgen/deploy.go index 84a06245132..4ef1d32b658 100644 --- a/op-chain-ops/interopgen/deploy.go +++ b/op-chain-ops/interopgen/deploy.go @@ -198,25 +198,24 @@ func DeployL2ToL1(l1Host *script.Host, superCfg *SuperchainConfig, superDeployme l1Host.SetTxOrigin(cfg.Deployer) output, err := opcm.DeployOPChain(l1Host, opcm.DeployOPChainInput{ - OpChainProxyAdminOwner: cfg.ProxyAdminOwner, - SystemConfigOwner: cfg.SystemConfigOwner, - Batcher: cfg.BatchSenderAddress, - UnsafeBlockSigner: cfg.P2PSequencerAddress, - Proposer: cfg.Proposer, - Challenger: cfg.Challenger, - BasefeeScalar: cfg.GasPriceOracleBaseFeeScalar, - BlobBaseFeeScalar: cfg.GasPriceOracleBlobBaseFeeScalar, - L2ChainId: new(big.Int).SetUint64(cfg.L2ChainID), - Opcm: superDeployment.Opcm, - SaltMixer: cfg.SaltMixer, - GasLimit: cfg.GasLimit, - DisputeGameUsesSuperRoots: cfg.DisputeGameUsesSuperRoots, - DisputeGameType: cfg.DisputeGameType, - DisputeAbsolutePrestate: cfg.DisputeAbsolutePrestate, - DisputeMaxGameDepth: cfg.DisputeMaxGameDepth, - DisputeSplitDepth: cfg.DisputeSplitDepth, - DisputeClockExtension: cfg.DisputeClockExtension, - DisputeMaxClockDuration: cfg.DisputeMaxClockDuration, + OpChainProxyAdminOwner: cfg.ProxyAdminOwner, + SystemConfigOwner: cfg.SystemConfigOwner, + Batcher: cfg.BatchSenderAddress, + UnsafeBlockSigner: cfg.P2PSequencerAddress, + Proposer: cfg.Proposer, + Challenger: cfg.Challenger, + BasefeeScalar: cfg.GasPriceOracleBaseFeeScalar, + BlobBaseFeeScalar: cfg.GasPriceOracleBlobBaseFeeScalar, + L2ChainId: new(big.Int).SetUint64(cfg.L2ChainID), + Opcm: superDeployment.Opcm, + SaltMixer: cfg.SaltMixer, + GasLimit: cfg.GasLimit, + DisputeGameType: cfg.DisputeGameType, + DisputeAbsolutePrestate: cfg.DisputeAbsolutePrestate, + DisputeMaxGameDepth: cfg.DisputeMaxGameDepth, + DisputeSplitDepth: cfg.DisputeSplitDepth, + DisputeClockExtension: cfg.DisputeClockExtension, + DisputeMaxClockDuration: cfg.DisputeMaxClockDuration, }) if err != nil { return nil, fmt.Errorf("failed to deploy L2 OP chain: %w", err) diff --git a/op-chain-ops/interopgen/recipe.go b/op-chain-ops/interopgen/recipe.go index b835716ea58..6cf3265925c 100644 --- a/op-chain-ops/interopgen/recipe.go +++ b/op-chain-ops/interopgen/recipe.go @@ -275,16 +275,15 @@ func (r *InteropDevL2Recipe) build(l1ChainID uint64, addrs devkeys.Addresses) (* UseAltDA: false, }, }, - Prefund: make(map[common.Address]*big.Int), - SaltMixer: "", - GasLimit: 60_000_000, - DisputeGameUsesSuperRoots: true, - DisputeGameType: 1, // PERMISSIONED_CANNON Game Type - DisputeAbsolutePrestate: common.HexToHash("0x038512e02c4c3f7bdaec27d00edf55b7155e0905301e1a88083e4e0a6764d54c"), - DisputeMaxGameDepth: 73, - DisputeSplitDepth: 30, - DisputeClockExtension: 10800, // 3 hours (input in seconds) - DisputeMaxClockDuration: 302400, // 3.5 days (input in seconds) + Prefund: make(map[common.Address]*big.Int), + SaltMixer: "", + GasLimit: 60_000_000, + DisputeGameType: 1, // PERMISSIONED_CANNON Game Type + DisputeAbsolutePrestate: common.HexToHash("0x038512e02c4c3f7bdaec27d00edf55b7155e0905301e1a88083e4e0a6764d54c"), + DisputeMaxGameDepth: 73, + DisputeSplitDepth: 30, + DisputeClockExtension: 10800, // 3 hours (input in seconds) + DisputeMaxClockDuration: 302400, // 3.5 days (input in seconds) } l2Users := devkeys.ChainUserKeys(new(big.Int).SetUint64(r.ChainID)) diff --git a/op-deployer/pkg/deployer/integration_test/apply_test.go b/op-deployer/pkg/deployer/integration_test/apply_test.go index 85679beaaae..8bdcf1d56ae 100644 --- a/op-deployer/pkg/deployer/integration_test/apply_test.go +++ b/op-deployer/pkg/deployer/integration_test/apply_test.go @@ -511,7 +511,6 @@ func TestAdditionalDisputeGames(t *testing.T) { intent.Chains[0].AdditionalDisputeGames = []state.AdditionalDisputeGame{ { ChainProofParams: state.ChainProofParams{ - DisputeGameUsesSuperRoots: false, DisputeGameType: 255, DisputeAbsolutePrestate: standard.DisputeAbsolutePrestate, DisputeMaxGameDepth: 50, diff --git a/op-deployer/pkg/deployer/opcm/opchain.go b/op-deployer/pkg/deployer/opcm/opchain.go index 813b7435170..9d87fb4979a 100644 --- a/op-deployer/pkg/deployer/opcm/opchain.go +++ b/op-deployer/pkg/deployer/opcm/opchain.go @@ -33,7 +33,6 @@ type DeployOPChainInput struct { SaltMixer string GasLimit uint64 - DisputeGameUsesSuperRoots bool DisputeGameType uint32 DisputeAbsolutePrestate common.Hash DisputeMaxGameDepth uint64 diff --git a/op-deployer/pkg/deployer/pipeline/opchain.go b/op-deployer/pkg/deployer/pipeline/opchain.go index 3007aaa1f47..fd6c6476678 100644 --- a/op-deployer/pkg/deployer/pipeline/opchain.go +++ b/op-deployer/pkg/deployer/pipeline/opchain.go @@ -74,13 +74,12 @@ func DeployOPChain(env *Env, intent *state.Intent, st *state.State, chainID comm func makeDCI(intent *state.Intent, thisIntent *state.ChainIntent, chainID common.Hash, st *state.State) (opcm.DeployOPChainInput, error) { proofParams, err := jsonutil.MergeJSON( state.ChainProofParams{ - DisputeGameUsesSuperRoots: standard.DisputeGameUsesSuperRoots, - DisputeGameType: standard.DisputeGameType, - DisputeAbsolutePrestate: standard.DisputeAbsolutePrestate, - DisputeMaxGameDepth: standard.DisputeMaxGameDepth, - DisputeSplitDepth: standard.DisputeSplitDepth, - DisputeClockExtension: standard.DisputeClockExtension, - DisputeMaxClockDuration: standard.DisputeMaxClockDuration, + DisputeGameType: standard.DisputeGameType, + DisputeAbsolutePrestate: standard.DisputeAbsolutePrestate, + DisputeMaxGameDepth: standard.DisputeMaxGameDepth, + DisputeSplitDepth: standard.DisputeSplitDepth, + DisputeClockExtension: standard.DisputeClockExtension, + DisputeMaxClockDuration: standard.DisputeMaxClockDuration, }, intent.GlobalDeployOverrides, thisIntent.DeployOverrides, @@ -102,7 +101,6 @@ func makeDCI(intent *state.Intent, thisIntent *state.ChainIntent, chainID common Opcm: st.ImplementationsDeployment.OpcmAddress, SaltMixer: st.Create2Salt.String(), // passing through salt generated at state initialization GasLimit: standard.GasLimit, - DisputeGameUsesSuperRoots: proofParams.DisputeGameUsesSuperRoots, DisputeGameType: proofParams.DisputeGameType, DisputeAbsolutePrestate: proofParams.DisputeAbsolutePrestate, DisputeMaxGameDepth: proofParams.DisputeMaxGameDepth, diff --git a/op-deployer/pkg/deployer/standard/standard.go b/op-deployer/pkg/deployer/standard/standard.go index 72f65d5d2ae..e9ba7d8d455 100644 --- a/op-deployer/pkg/deployer/standard/standard.go +++ b/op-deployer/pkg/deployer/standard/standard.go @@ -26,7 +26,6 @@ const ( ProofMaturityDelaySeconds uint64 = 604800 DisputeGameFinalityDelaySeconds uint64 = 302400 MIPSVersion uint64 = 1 - DisputeGameUsesSuperRoots bool = false DisputeGameType uint32 = 1 // PERMISSIONED game type DisputeMaxGameDepth uint64 = 73 DisputeSplitDepth uint64 = 30 diff --git a/op-deployer/pkg/deployer/state/chain_intent.go b/op-deployer/pkg/deployer/state/chain_intent.go index b53a8d0a8bc..f374466ea41 100644 --- a/op-deployer/pkg/deployer/state/chain_intent.go +++ b/op-deployer/pkg/deployer/state/chain_intent.go @@ -17,7 +17,6 @@ const ( ) type ChainProofParams struct { - DisputeGameUsesSuperRoots bool `json:"disputeGameUsesSuperRoots" toml:"disputeGameUsesSuperRoots"` DisputeGameType uint32 `json:"respectedGameType" toml:"respectedGameType"` DisputeAbsolutePrestate common.Hash `json:"faultGameAbsolutePrestate" toml:"faultGameAbsolutePrestate"` DisputeMaxGameDepth uint64 `json:"faultGameMaxDepth" toml:"faultGameMaxDepth"` diff --git a/op-deployer/pkg/deployer/upgrade/v2_0_0/upgrade_test.go b/op-deployer/pkg/deployer/upgrade/v2_0_0/upgrade_test.go index b025855751b..95585401b11 100644 --- a/op-deployer/pkg/deployer/upgrade/v2_0_0/upgrade_test.go +++ b/op-deployer/pkg/deployer/upgrade/v2_0_0/upgrade_test.go @@ -21,7 +21,6 @@ import ( ) func TestUpgradeOPChainInput_OpChainConfigs(t *testing.T) { - t.Skip("skipping for upgrade 15") input := &UpgradeOPChainInput{ Prank: common.Address{0xaa}, Opcm: common.Address{0xbb}, @@ -55,7 +54,6 @@ func TestUpgradeOPChainInput_OpChainConfigs(t *testing.T) { } func TestUpgrader_Upgrade(t *testing.T) { - t.Skip("skipping for upgrade 15") lgr := testlog.Logger(t, slog.LevelDebug) forkedL1, stopL1, err := devnet.NewForkedSepolia(lgr) diff --git a/op-e2e/actions/helpers/l2_batcher.go b/op-e2e/actions/helpers/l2_batcher.go index 3e67166d92f..833572e6971 100644 --- a/op-e2e/actions/helpers/l2_batcher.go +++ b/op-e2e/actions/helpers/l2_batcher.go @@ -528,3 +528,49 @@ func (s *L2Batcher) ActSubmitAllMultiBlobs(t Testing, numBlobs int) { s.ActL2ChannelClose(t) s.ActL2BatchSubmitMultiBlob(t, numBlobs) } + +// ActSubmitSetCodeTx submits a SetCodeTx to the batch inbox. This models a malicious +// batcher and is only used to tests the derivation pipeline follows spec and ignores +// the SetCodeTx. +func (s *L2Batcher) ActSubmitSetCodeTx(t Testing) { + chainId := *uint256.MustFromBig(s.rollupCfg.L1ChainID) + + nonce, err := s.l1.PendingNonceAt(t.Ctx(), s.BatcherAddr) + require.NoError(t, err, "need batcher nonce") + + tx, err := PrepareSignedSetCodeTx(chainId, s.l2BatcherCfg.BatcherKey, s.l1Signer, nonce, s.rollupCfg.BatchInboxAddress, s.ReadNextOutputFrame(t)) + require.NoError(t, err, "need to sign tx") + + t.Log("submitting EIP 7702 Set Code Batcher Transaction...") + err = s.l1.SendTransaction(t.Ctx(), tx) + require.NoError(t, err, "need to send tx") + s.LastSubmitted = tx +} + +func PrepareSignedSetCodeTx(chainId uint256.Int, privateKey *ecdsa.PrivateKey, signer types.Signer, nonce uint64, to common.Address, data []byte) (*types.Transaction, error) { + + setCodeAuthorization := types.SetCodeAuthorization{ + ChainID: chainId, + Address: common.HexToAddress("0xab"), // arbitrary nonzero address + Nonce: nonce, + } + + signedAuth, err := types.SignSetCode(privateKey, setCodeAuthorization) + if err != nil { + return nil, err + } + + txData := &types.SetCodeTx{ + ChainID: &chainId, + Nonce: nonce, + To: to, + Value: uint256.NewInt(0), + Data: data, + AccessList: types.AccessList{}, + AuthList: []types.SetCodeAuthorization{signedAuth}, + Gas: 1_000_000, + GasFeeCap: uint256.NewInt(1_000_000_000), + } + + return types.SignNewTx(privateKey, signer, txData) +} diff --git a/op-e2e/actions/proofs/helpers/matrix.go b/op-e2e/actions/proofs/helpers/matrix.go index c679f035841..44612f7c708 100644 --- a/op-e2e/actions/proofs/helpers/matrix.go +++ b/op-e2e/actions/proofs/helpers/matrix.go @@ -81,15 +81,24 @@ func (ts *TestMatrix[cfg]) AddDefaultTestCases( testCfg cfg, forkMatrix ForkMatrix, runTest RunTest[cfg], +) *TestMatrix[cfg] { + return ts.AddDefaultTestCasesWithName("", testCfg, forkMatrix, runTest) +} + +func (ts *TestMatrix[cfg]) AddDefaultTestCasesWithName( + name string, + testCfg cfg, + forkMatrix ForkMatrix, + runTest RunTest[cfg], ) *TestMatrix[cfg] { return ts.AddTestCase( - "HonestClaim", + "HonestClaim-"+name, testCfg, forkMatrix, runTest, ExpectNoError(), ).AddTestCase( - "JunkClaim", + "JunkClaim-"+name, testCfg, forkMatrix, runTest, diff --git a/op-e2e/actions/proofs/l1_prague_fork_test.go b/op-e2e/actions/proofs/l1_prague_fork_test.go new file mode 100644 index 00000000000..1ec028a69bd --- /dev/null +++ b/op-e2e/actions/proofs/l1_prague_fork_test.go @@ -0,0 +1,186 @@ +package proofs_test + +import ( + "testing" + + batcherFlags "github.com/ethereum-optimism/optimism/op-batcher/flags" + "github.com/ethereum-optimism/optimism/op-chain-ops/genesis" + actionsHelpers "github.com/ethereum-optimism/optimism/op-e2e/actions/helpers" + "github.com/ethereum-optimism/optimism/op-e2e/actions/proofs/helpers" + legacybindings "github.com/ethereum-optimism/optimism/op-e2e/bindings" + "github.com/ethereum-optimism/optimism/op-service/eth" + "github.com/ethereum-optimism/optimism/op-service/predeploys" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/consensus/misc/eip4844" + "github.com/ethereum/go-ethereum/core/types" + "github.com/holiman/uint256" + "github.com/stretchr/testify/require" +) + +func TestPragueForkAfterGenesis(gt *testing.T) { + type testCase struct { + name string + useSetCodeTx bool + } + + dynamiceFeeCase := testCase{ + name: "dynamicFeeTx", useSetCodeTx: false, + } + setCodeCase := testCase{ + name: "setCodeTx", useSetCodeTx: true, + } + + runL1PragueTest := func(gt *testing.T, testCfg *helpers.TestCfg[testCase]) { + t := actionsHelpers.NewDefaultTesting(gt) + env := helpers.NewL2FaultProofEnv(t, testCfg, helpers.NewTestParams(), + helpers.NewBatcherCfg( + func(c *actionsHelpers.BatcherCfg) { + c.DataAvailabilityType = batcherFlags.CalldataType + }, + ), + func(dp *genesis.DeployConfig) { + dp.L1PragueTimeOffset = ptr(hexutil.Uint64(24)) // Activate at second l1 block + }, + ) + + miner, batcher, verifier, sequencer, engine := env.Miner, env.Batcher, env.Sequencer, env.Sequencer, env.Engine + + l1Block, err := legacybindings.NewL1Block(predeploys.L1BlockAddr, engine.EthClient()) + require.NoError(t, err) + + // utils + checkVerifierDerivedToL1Head := func(t actionsHelpers.StatefulTesting) { + l1Head := miner.L1Chain().CurrentBlock() + currentL1 := verifier.SyncStatus().CurrentL1 + require.Equal(t, l1Head.Number.Int64(), int64(currentL1.Number), "verifier should derive up to and including the L1 head") + require.Equal(t, l1Head.Hash(), currentL1.Hash, "verifier should derive up to and including the L1 head") + } + + buildUnsafeL2AndSubmit := func(useSetCode bool) { + sequencer.ActL1HeadSignal(t) + sequencer.ActBuildToL1Head(t) + + miner.ActL1StartBlock(12)(t) + if useSetCode { + batcher.ActBufferAll(t) + batcher.ActL2ChannelClose(t) + batcher.ActSubmitSetCodeTx(t) + } else { + batcher.ActSubmitAll(t) + } + miner.ActL1IncludeTx(batcher.BatcherAddr)(t) + miner.ActL1EndBlock(t) + } + + requirePragueStatusOnL1 := func(active bool, block *types.Header) { + if active { + require.True(t, env.Sd.L1Cfg.Config.IsPrague(block.Number, block.Time), "Prague should be active at block", block.Number.Uint64()) + require.NotNil(t, block.RequestsHash, "Prague header requests hash should be non-nil") + } else { + require.False(t, env.Sd.L1Cfg.Config.IsPrague(block.Number, block.Time), "Prague should not be active yet at block", block.Number.Uint64()) + require.Nil(t, block.RequestsHash, "Prague header requests hash should be nil") + } + } + + syncVerifierAndCheck := func(t actionsHelpers.StatefulTesting) { + verifier.ActL1HeadSignal(t) + verifier.ActL2PipelineFull(t) + checkVerifierDerivedToL1Head(t) + } + + checkL1BlockBlobBaseFee := func(t actionsHelpers.StatefulTesting, l2Block eth.L2BlockRef) { + l1BlockID := l2Block.L1Origin + l1BlockHeader := miner.L1Chain().GetHeaderByHash(l1BlockID.Hash) + expectedBbf := eth.CalcBlobFeeDefault(l1BlockHeader) + upstreamExpectedBbf := eip4844.CalcBlobFee(env.Sd.L1Cfg.Config, l1BlockHeader) + require.Equal(t, expectedBbf.Uint64(), upstreamExpectedBbf.Uint64(), "expected blob base fee should match upstream calculation") + bbf, err := l1Block.BlobBaseFee(&bind.CallOpts{BlockHash: l2Block.Hash}) + require.NoError(t, err, "failed to get blob base fee") + require.Equal(t, expectedBbf.Uint64(), bbf.Uint64(), "l1Block blob base fee does not match expectation, l1BlockNum %d, l2BlockNum %d", l1BlockID.Number, l2Block.Number) + } + + requireSafeHeadProgression := func(t actionsHelpers.StatefulTesting, safeL2Before, safeL2After eth.L2BlockRef, batchedWithSetCodeTx bool) { + if batchedWithSetCodeTx { + require.Equal(t, safeL2Before, safeL2After, "safe head should not have changed (SetCode / type 4 batcher tx ignored)") + require.Equal(t, safeL2Before.L1Origin.Number, safeL2After.Number, "l1 origin of l2 safe should not have changed (SetCode / type 4 batcher tx ignored)") + } else { + require.Greater(t, safeL2After.Number, safeL2Before.Number, "safe head should have progressed (DynamicFee / type 2 batcher tx derived from)") + require.Equal(t, verifier.SyncStatus().UnsafeL2.Number, safeL2After.Number, "safe head should equal unsafe head (DynamicFee / type 2 batcher tx derived from)") + require.Greater(t, safeL2After.L1Origin.Number, safeL2Before.L1Origin.Number, "l1 origin of l2 safe should have progressed (DynamicFee / type 2 batcher tx tx derived from)") + } + } + + // Check initially Prague is not activated + requirePragueStatusOnL1(false, miner.L1Chain().CurrentBlock()) + + // Start op-nodes + sequencer.ActL2PipelineFull(t) + verifier.ActL2PipelineFull(t) + + // Build L1 blocks, crossing the fork boundary + miner.ActEmptyBlock(t) // block 1 + miner.ActEmptyBlock(t) // Prague activates here (block 2) + + // Here's a block with a type 4 deposit transaction, sent to the OptimismPortal + miner.ActL1StartBlock(12)(t) // block 3 + tx, err := actionsHelpers.PrepareSignedSetCodeTx( + *uint256.MustFromBig(env.Sd.L1Cfg.Config.ChainID), + env.Dp.Secrets.Alice, + env.Alice.L1.Signer(), + env.Alice.L1.PendingNonce(t), // nonce + env.Sd.DeploymentsL1.OptimismPortalProxy, + []byte{}) + require.NoError(t, err, "failed to prepare set code tx") + err = miner.EthClient().SendTransaction(t.Ctx(), tx) + require.NoError(t, err, "failed to send set code tx") + miner.ActL1IncludeTx(env.Alice.Address())(t) + miner.ActL1EndBlock(t) + + // Check that Prague is active on L1 + requirePragueStatusOnL1(true, miner.L1Chain().CurrentBlock()) + + // Cache safe head before verifier sync + safeL2Initial := verifier.SyncStatus().SafeL2 + + // Build an empty L2 block which has a pre-prague L1 origin, and check the blob fee is correct + sequencer.ActL2EmptyBlock(t) + l1OriginHeader := miner.L1Chain().GetHeaderByHash(verifier.SyncStatus().UnsafeL2.L1Origin.Hash) + requirePragueStatusOnL1(false, l1OriginHeader) + checkL1BlockBlobBaseFee(t, verifier.SyncStatus().UnsafeL2) + + // Build L2 unsafe chain and batch it to L1 using either DynamicFee or + // EIP-7702 SetCode txs + // https://github.com/ethereum/EIPs/blob/master/EIPS/eip-7702.md + buildUnsafeL2AndSubmit(testCfg.Custom.useSetCodeTx) + + // Check verifier derived from Prague L1 blocks + syncVerifierAndCheck(t) + + // Check safe head did or did not change, + // depending on tx type used by batcher: + safeL2AfterFirstBatch := verifier.SyncStatus().SafeL2 + requireSafeHeadProgression(t, safeL2Initial, safeL2AfterFirstBatch, testCfg.Custom.useSetCodeTx) + + sequencer.ActBuildToL1Head(t) // Advance L2 chain until L1 origin has Prague active + + // Check that the l1 origin is now a Prague block, and that the blob fee is correct + l1Origin := miner.L1Chain().GetHeaderByNumber(verifier.SyncStatus().UnsafeL2.L1Origin.Number) + requirePragueStatusOnL1(true, l1Origin) + checkL1BlockBlobBaseFee(t, verifier.SyncStatus().UnsafeL2) + + // Batch and sync again + buildUnsafeL2AndSubmit(testCfg.Custom.useSetCodeTx) + syncVerifierAndCheck(t) + safeL2AfterSecondBatch := verifier.SyncStatus().SafeL2 + requireSafeHeadProgression(t, safeL2AfterFirstBatch, safeL2AfterSecondBatch, testCfg.Custom.useSetCodeTx) + + env.RunFaultProofProgram(t, safeL2AfterSecondBatch.Number, testCfg.CheckResult, testCfg.InputParams...) + } + + matrix := helpers.NewMatrix[testCase]() + defer matrix.Run(gt) + matrix. + AddDefaultTestCasesWithName(dynamiceFeeCase.name, dynamiceFeeCase, helpers.NewForkMatrix(helpers.Holocene, helpers.LatestFork), runL1PragueTest). + AddDefaultTestCasesWithName(setCodeCase.name, setCodeCase, helpers.NewForkMatrix(helpers.Holocene, helpers.LatestFork), runL1PragueTest) +} diff --git a/op-e2e/actions/upgrades/isthmus_fork_test.go b/op-e2e/actions/upgrades/isthmus_fork_test.go index 21759709210..f98f5691786 100644 --- a/op-e2e/actions/upgrades/isthmus_fork_test.go +++ b/op-e2e/actions/upgrades/isthmus_fork_test.go @@ -23,15 +23,16 @@ import ( "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/ethclient" "github.com/ethereum/go-ethereum/log" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) var ( - isthmusL1BlockCodeHash = common.HexToHash("0xe59074b8d4c08924ce463087b05485b835650652528383a32ef009fb2b6d4050") - isthmusGasPriceOracleCodeHash = common.HexToHash("0x9279e9e0535a7b63939d670e7faec536256b63d4ff353eb521a3342c51ce26e5") - isthmusOperatorFeeVaultCodeHash = common.HexToHash("0x9ee0fa5ab86f13f58fcb8f798f3a74401a8493d99d1c5b3bad19a8dff4b3194f") + isthmusL1BlockCodeHash = common.HexToHash("0x8e3fe7a416d3e5f3b7be74ddd4e7e58e516fa3f80b67c6d930e3cd7297da4a4b") + isthmusGasPriceOracleCodeHash = common.HexToHash("0x4d195a9d7caf9fb6d4beaf80de252c626c853afd5868c4f4f8d19c9d301c2679") + isthmusOperatorFeeVaultCodeHash = common.HexToHash("0x57dc55c9c09ca456fa728f253fe7b895d3e6aae0706104935fe87c7721001971") ) func TestIsthmusActivationAtGenesis(gt *testing.T) { @@ -321,6 +322,16 @@ func verifyIsthmusHeaderWithdrawalsRoot(gt *testing.T, rpcCl client.RPC, header } } +func checkContractVersion(gt *testing.T, client *ethclient.Client, addr common.Address, expectedVersion string) { + isemver, err := bindings.NewISemver(addr, client) + require.NoError(gt, err) + + version, err := isemver.Version(nil) + require.NoError(gt, err) + + require.Equal(gt, expectedVersion, version) +} + func TestIsthmusNetworkUpgradeTransactions(gt *testing.T) { t := helpers.NewDefaultTesting(gt) dp := e2eutils.MakeDeployParams(t, helpers.DefaultRollupTestParams()) @@ -429,6 +440,11 @@ func TestIsthmusNetworkUpgradeTransactions(gt *testing.T) { require.Equal(t, expectedHash, common.BytesToHash(rootValue), msg) } + // Check contract versions + checkContractVersion(gt, ethCl, common.BytesToAddress(updatedL1BlockAddress), "1.6.0") + checkContractVersion(gt, ethCl, common.BytesToAddress(updatedGasPriceOracleAddress), "1.4.0") + checkContractVersion(gt, ethCl, common.BytesToAddress(updatedOperatorFeeVaultAddress), "1.0.0") + // Legacy check: // > The first block is an exception in upgrade-networks, // > since the recent-block-hash contract isn't there at Isthmus activation, diff --git a/op-e2e/config/init.go b/op-e2e/config/init.go index 74c54093a2e..b13a6002342 100644 --- a/op-e2e/config/init.go +++ b/op-e2e/config/init.go @@ -436,13 +436,12 @@ func defaultIntent(root string, loc *artifacts.Locator, deployer common.Address, { ChainProofParams: state.ChainProofParams{ // Fast game - DisputeGameUsesSuperRoots: false, - DisputeGameType: 254, - DisputeAbsolutePrestate: defaultPrestate, - DisputeMaxGameDepth: 14 + 3 + 1, - DisputeSplitDepth: 14, - DisputeClockExtension: 0, - DisputeMaxClockDuration: 0, + DisputeGameType: 254, + DisputeAbsolutePrestate: defaultPrestate, + DisputeMaxGameDepth: 14 + 3 + 1, + DisputeSplitDepth: 14, + DisputeClockExtension: 0, + DisputeMaxClockDuration: 0, }, VMType: state.VMTypeAlphabet, UseCustomOracle: true, @@ -453,25 +452,23 @@ func defaultIntent(root string, loc *artifacts.Locator, deployer common.Address, { ChainProofParams: state.ChainProofParams{ // Alphabet game - DisputeGameUsesSuperRoots: false, - DisputeGameType: 255, - DisputeAbsolutePrestate: defaultPrestate, - DisputeMaxGameDepth: 14 + 3 + 1, - DisputeSplitDepth: 14, - DisputeClockExtension: 0, - DisputeMaxClockDuration: 1200, + DisputeGameType: 255, + DisputeAbsolutePrestate: defaultPrestate, + DisputeMaxGameDepth: 14 + 3 + 1, + DisputeSplitDepth: 14, + DisputeClockExtension: 0, + DisputeMaxClockDuration: 1200, }, VMType: state.VMTypeAlphabet, }, { ChainProofParams: state.ChainProofParams{ - DisputeGameUsesSuperRoots: false, - DisputeGameType: 0, - DisputeAbsolutePrestate: cannonPrestate(root, allocType), - DisputeMaxGameDepth: 50, - DisputeSplitDepth: 14, - DisputeClockExtension: 0, - DisputeMaxClockDuration: 1200, + DisputeGameType: 0, + DisputeAbsolutePrestate: cannonPrestate(root, allocType), + DisputeMaxGameDepth: 50, + DisputeSplitDepth: 14, + DisputeClockExtension: 0, + DisputeMaxClockDuration: 1200, }, VMType: cannonVMType(allocType), }, diff --git a/op-node/rollup/derive/isthmus_upgrade_transactions.go b/op-node/rollup/derive/isthmus_upgrade_transactions.go index 31c575af469..25bee50b061 100644 --- a/op-node/rollup/derive/isthmus_upgrade_transactions.go +++ b/op-node/rollup/derive/isthmus_upgrade_transactions.go @@ -30,9 +30,9 @@ var ( OperatorFeeVaultAddress = crypto.CreateAddress(OperatorFeeVaultDeployerAddress, 0) // Bytecodes - l1BlockIsthmusDeploymentBytecode = common.FromHex("0x608060405234801561001057600080fd5b506106ae806100206000396000f3fe608060405234801561001057600080fd5b50600436106101825760003560e01c806364ca23ef116100d8578063b80777ea1161008c578063e591b28211610066578063e591b282146103b0578063e81b2c6d146103d2578063f8206140146103db57600080fd5b8063b80777ea14610337578063c598591814610357578063d84447151461037757600080fd5b80638381f58a116100bd5780638381f58a146103115780638b239f73146103255780639e8c49661461032e57600080fd5b806364ca23ef146102e157806368d5dca6146102f557600080fd5b80634397dfef1161013a57806354fd4d501161011457806354fd4d501461025d578063550fcdc91461029f5780635cf24969146102d857600080fd5b80634397dfef146101fc578063440a5e20146102245780634d5d9a2a1461022c57600080fd5b806309bd5a601161016b57806309bd5a60146101a457806316d3bc7f146101c057806321326849146101ed57600080fd5b8063015d8eb914610187578063098999be1461019c575b600080fd5b61019a6101953660046105bc565b6103e4565b005b61019a610523565b6101ad60025481565b6040519081526020015b60405180910390f35b6008546101d49067ffffffffffffffff1681565b60405167ffffffffffffffff90911681526020016101b7565b604051600081526020016101b7565b6040805173eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee815260126020820152016101b7565b61019a61052d565b6008546102489068010000000000000000900463ffffffff1681565b60405163ffffffff90911681526020016101b7565b60408051808201909152600c81527f312e352e312d626574612e37000000000000000000000000000000000000000060208201525b6040516101b7919061062e565b60408051808201909152600381527f45544800000000000000000000000000000000000000000000000000000000006020820152610292565b6101ad60015481565b6003546101d49067ffffffffffffffff1681565b6003546102489068010000000000000000900463ffffffff1681565b6000546101d49067ffffffffffffffff1681565b6101ad60055481565b6101ad60065481565b6000546101d49068010000000000000000900467ffffffffffffffff1681565b600354610248906c01000000000000000000000000900463ffffffff1681565b60408051808201909152600581527f45746865720000000000000000000000000000000000000000000000000000006020820152610292565b60405173deaddeaddeaddeaddeaddeaddeaddeaddead000181526020016101b7565b6101ad60045481565b6101ad60075481565b3373deaddeaddeaddeaddeaddeaddeaddeaddead00011461048b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603b60248201527f4c31426c6f636b3a206f6e6c7920746865206465706f7369746f72206163636f60448201527f756e742063616e20736574204c3120626c6f636b2076616c7565730000000000606482015260840160405180910390fd5b6000805467ffffffffffffffff98891668010000000000000000027fffffffffffffffffffffffffffffffff00000000000000000000000000000000909116998916999099179890981790975560019490945560029290925560038054919094167fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000009190911617909255600491909155600555600655565b61052b610535565b565b61052b610548565b61053d610548565b60a43560a01c600855565b73deaddeaddeaddeaddeaddeaddeaddeaddead000133811461057257633cc50b456000526004601cfd5b60043560801c60035560143560801c60005560243560015560443560075560643560025560843560045550565b803567ffffffffffffffff811681146105b757600080fd5b919050565b600080600080600080600080610100898b0312156105d957600080fd5b6105e28961059f565b97506105f060208a0161059f565b9650604089013595506060890135945061060c60808a0161059f565b979a969950949793969560a0850135955060c08501359460e001359350915050565b600060208083528351808285015260005b8181101561065b5785810183015185820160400152820161063f565b8181111561066d576000604083870101525b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01692909201604001939250505056fea164736f6c634300080f000a") - gasPriceOracleIsthmusDeploymentBytecode = common.FromHex("0x608060405234801561001057600080fd5b50611c3c806100206000396000f3fe608060405234801561001057600080fd5b50600436106101775760003560e01c806368d5dca6116100d8578063c59859181161008c578063f45e65d811610066578063f45e65d8146102ca578063f8206140146102d2578063fe173b971461026957600080fd5b8063c59859181461029c578063de26c4a1146102a4578063f1c7a58b146102b757600080fd5b80638e98b106116100bd5780638e98b1061461026f578063960e3a2314610277578063b54501bc1461028957600080fd5b806368d5dca61461024c5780636ef25c3a1461026957600080fd5b8063313ce5671161012f5780634ef6e224116101145780634ef6e224146101de578063519b4bd3146101fb57806354fd4d501461020357600080fd5b8063313ce567146101c457806349948e0e146101cb57600080fd5b8063275aedd211610160578063275aedd2146101a1578063291b0383146101b45780632e0f2625146101bc57600080fd5b80630c18c1621461017c57806322b90ab314610197575b600080fd5b6101846102da565b6040519081526020015b60405180910390f35b61019f6103fb565b005b6101846101af36600461168e565b610584565b61019f61070f565b610184600681565b6006610184565b6101846101d93660046116d6565b610937565b6000546101eb9060ff1681565b604051901515815260200161018e565b61018461096e565b61023f6040518060400160405280600c81526020017f312e332e312d626574612e35000000000000000000000000000000000000000081525081565b60405161018e91906117a5565b6102546109cf565b60405163ffffffff909116815260200161018e565b48610184565b61019f610a54565b6000546101eb90610100900460ff1681565b6000546101eb9062010000900460ff1681565b610254610c4e565b6101846102b23660046116d6565b610caf565b6101846102c536600461168e565b610da9565b610184610e85565b610184610f78565b6000805460ff1615610373576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602860248201527f47617350726963654f7261636c653a206f76657268656164282920697320646560448201527f707265636174656400000000000000000000000000000000000000000000000060648201526084015b60405180910390fd5b73420000000000000000000000000000000000001573ffffffffffffffffffffffffffffffffffffffff16638b239f736040518163ffffffff1660e01b8152600401602060405180830381865afa1580156103d2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103f69190611818565b905090565b3373deaddeaddeaddeaddeaddeaddeaddeaddead0001146104c4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604160248201527f47617350726963654f7261636c653a206f6e6c7920746865206465706f73697460448201527f6f72206163636f756e742063616e2073657420697345636f746f6e6520666c6160648201527f6700000000000000000000000000000000000000000000000000000000000000608482015260a40161036a565b60005460ff1615610557576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f47617350726963654f7261636c653a2045636f746f6e6520616c72656164792060448201527f6163746976650000000000000000000000000000000000000000000000000000606482015260840161036a565b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055565b6000805462010000900460ff1661059d57506000919050565b610709620f42406106668473420000000000000000000000000000000000001573ffffffffffffffffffffffffffffffffffffffff16634d5d9a2a6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610607573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061062b9190611831565b63ffffffff167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff821583830293840490921491909117011790565b6106709190611886565b73420000000000000000000000000000000000001573ffffffffffffffffffffffffffffffffffffffff166316d3bc7f6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156106cf573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106f391906118c1565b67ffffffffffffffff1681019081106000031790565b92915050565b3373deaddeaddeaddeaddeaddeaddeaddeaddead0001146107d8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604160248201527f47617350726963654f7261636c653a206f6e6c7920746865206465706f73697460448201527f6f72206163636f756e742063616e20736574206973497374686d757320666c6160648201527f6700000000000000000000000000000000000000000000000000000000000000608482015260a40161036a565b600054610100900460ff1661086f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603960248201527f47617350726963654f7261636c653a20497374686d75732063616e206f6e6c7960448201527f2062652061637469766174656420616674657220466a6f726400000000000000606482015260840161036a565b60005462010000900460ff1615610908576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f47617350726963654f7261636c653a20497374686d757320616c72656164792060448201527f6163746976650000000000000000000000000000000000000000000000000000606482015260840161036a565b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffff1662010000179055565b60008054610100900460ff16156109515761070982610fd9565b60005460ff16156109655761070982610ff8565b6107098261109c565b600073420000000000000000000000000000000000001573ffffffffffffffffffffffffffffffffffffffff16635cf249696040518163ffffffff1660e01b8152600401602060405180830381865afa1580156103d2573d6000803e3d6000fd5b600073420000000000000000000000000000000000001573ffffffffffffffffffffffffffffffffffffffff166368d5dca66040518163ffffffff1660e01b8152600401602060405180830381865afa158015610a30573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103f69190611831565b3373deaddeaddeaddeaddeaddeaddeaddeaddead000114610af7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603f60248201527f47617350726963654f7261636c653a206f6e6c7920746865206465706f73697460448201527f6f72206163636f756e742063616e20736574206973466a6f726420666c616700606482015260840161036a565b60005460ff16610b89576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603960248201527f47617350726963654f7261636c653a20466a6f72642063616e206f6e6c79206260448201527f65206163746976617465642061667465722045636f746f6e6500000000000000606482015260840161036a565b600054610100900460ff1615610c20576040517f08c379a0000000000000000000000000000000000000000000000000000000008152602060048201526024808201527f47617350726963654f7261636c653a20466a6f726420616c726561647920616360448201527f7469766500000000000000000000000000000000000000000000000000000000606482015260840161036a565b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff16610100179055565b600073420000000000000000000000000000000000001573ffffffffffffffffffffffffffffffffffffffff1663c59859186040518163ffffffff1660e01b8152600401602060405180830381865afa158015610a30573d6000803e3d6000fd5b60008054610100900460ff1615610cf657620f4240610ce1610cd0846111f0565b51610cdc9060446118eb565b61150d565b610cec906010611903565b6107099190611886565b6000610d018361156c565b60005490915060ff1615610d155792915050565b73420000000000000000000000000000000000001573ffffffffffffffffffffffffffffffffffffffff16638b239f736040518163ffffffff1660e01b8152600401602060405180830381865afa158015610d74573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d989190611818565b610da290826118eb565b9392505050565b60008054610100900460ff16610e41576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603660248201527f47617350726963654f7261636c653a206765744c314665655570706572426f7560448201527f6e64206f6e6c7920737570706f72747320466a6f726400000000000000000000606482015260840161036a565b6000610e4e8360446118eb565b90506000610e5d60ff83611886565b610e6790836118eb565b610e729060106118eb565b9050610e7d816115fc565b949350505050565b6000805460ff1615610f19576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f47617350726963654f7261636c653a207363616c61722829206973206465707260448201527f6563617465640000000000000000000000000000000000000000000000000000606482015260840161036a565b73420000000000000000000000000000000000001573ffffffffffffffffffffffffffffffffffffffff16639e8c49666040518163ffffffff1660e01b8152600401602060405180830381865afa1580156103d2573d6000803e3d6000fd5b600073420000000000000000000000000000000000001573ffffffffffffffffffffffffffffffffffffffff1663f82061406040518163ffffffff1660e01b8152600401602060405180830381865afa1580156103d2573d6000803e3d6000fd5b6000610709610fe7836111f0565b51610ff39060446118eb565b6115fc565b6000806110048361156c565b9050600061101061096e565b611018610c4e565b611023906010611940565b63ffffffff166110339190611903565b9050600061103f610f78565b6110476109cf565b63ffffffff166110579190611903565b9050600061106582846118eb565b61106f9085611903565b905061107d6006600a611a8c565b611088906010611903565b6110929082611886565b9695505050505050565b6000806110a88361156c565b9050600073420000000000000000000000000000000000001573ffffffffffffffffffffffffffffffffffffffff16639e8c49666040518163ffffffff1660e01b8152600401602060405180830381865afa15801561110b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061112f9190611818565b61113761096e565b73420000000000000000000000000000000000001573ffffffffffffffffffffffffffffffffffffffff16638b239f736040518163ffffffff1660e01b8152600401602060405180830381865afa158015611196573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111ba9190611818565b6111c490856118eb565b6111ce9190611903565b6111d89190611903565b90506111e66006600a611a8c565b610e7d9082611886565b606061137f565b818153600101919050565b600082840393505b83811015610da25782810151828201511860001a159093029260010161120a565b825b60208210611277578251611242601f836111f7565b52602092909201917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09091019060210161122d565b8115610da257825161128c60018403836111f7565b520160010192915050565b60006001830392505b61010782106112d8576112ca8360ff166112c560fd6112c58760081c60e001896111f7565b6111f7565b9350610106820391506112a0565b60078210611305576112fe8360ff166112c5600785036112c58760081c60e001896111f7565b9050610da2565b610e7d8360ff166112c58560081c8560051b01876111f7565b61137782820361135b61134b84600081518060001a8160011a60081b178160021a60101b17915050919050565b639e3779b90260131c611fff1690565b8060021b6040510182815160e01c1860e01b8151188152505050565b600101919050565b6180003860405139618000604051016020830180600d8551820103826002015b818110156114b2576000805b50508051604051600082901a600183901a60081b1760029290921a60101b91909117639e3779b9810260111c617ffc16909101805160e081811c878603811890911b909118909152840190818303908484106114075750611442565b600184019350611fff821161143c578251600081901a600182901a60081b1760029190911a60101b17810361143c5750611442565b506113ab565b8383106114505750506114b2565b6001830392508583111561146e5761146b878788860361122b565b96505b611482600985016003850160038501611202565b915061148f878284611297565b9650506114a7846114a28684860161131e565b61131e565b91505080935061139f565b50506114c4838384885185010361122b565b925050506040519150618000820180820391508183526020830160005b838110156114f95782810151828201526020016114e1565b506000920191825250602001604052919050565b60008061151d83620cc394611903565b611547907ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd763200611a98565b90506115576064620f4240611b0c565b81121561070957610da26064620f4240611b0c565b80516000908190815b818110156115ef5784818151811061158f5761158f611bc8565b01602001517fff00000000000000000000000000000000000000000000000000000000000000166000036115cf576115c86004846118eb565b92506115dd565b6115da6010846118eb565b92505b806115e781611bf7565b915050611575565b50610e7d826104406118eb565b6000806116088361150d565b90506000611614610f78565b61161c6109cf565b63ffffffff1661162c9190611903565b61163461096e565b61163c610c4e565b611647906010611940565b63ffffffff166116579190611903565b61166191906118eb565b905061166f60066002611903565b61167a90600a611a8c565b6116848284611903565b610e7d9190611886565b6000602082840312156116a057600080fd5b5035919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6000602082840312156116e857600080fd5b813567ffffffffffffffff8082111561170057600080fd5b818401915084601f83011261171457600080fd5b813581811115611726576117266116a7565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f0116810190838211818310171561176c5761176c6116a7565b8160405282815287602084870101111561178557600080fd5b826020860160208301376000928101602001929092525095945050505050565b600060208083528351808285015260005b818110156117d2578581018301518582016040015282016117b6565b818111156117e4576000604083870101525b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016929092016040019392505050565b60006020828403121561182a57600080fd5b5051919050565b60006020828403121561184357600080fd5b815163ffffffff81168114610da257600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000826118bc577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b6000602082840312156118d357600080fd5b815167ffffffffffffffff81168114610da257600080fd5b600082198211156118fe576118fe611857565b500190565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048311821515161561193b5761193b611857565b500290565b600063ffffffff8083168185168183048111821515161561196357611963611857565b02949350505050565b600181815b808511156119c557817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048211156119ab576119ab611857565b808516156119b857918102915b93841c9390800290611971565b509250929050565b6000826119dc57506001610709565b816119e957506000610709565b81600181146119ff5760028114611a0957611a25565b6001915050610709565b60ff841115611a1a57611a1a611857565b50506001821b610709565b5060208310610133831016604e8410600b8410161715611a48575081810a610709565b611a52838361196c565b807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04821115611a8457611a84611857565b029392505050565b6000610da283836119cd565b6000808212827f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff03841381151615611ad257611ad2611857565b827f8000000000000000000000000000000000000000000000000000000000000000038412811615611b0657611b06611857565b50500190565b60007f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600084136000841385830485118282161615611b4d57611b4d611857565b7f80000000000000000000000000000000000000000000000000000000000000006000871286820588128184161615611b8857611b88611857565b60008712925087820587128484161615611ba457611ba4611857565b87850587128184161615611bba57611bba611857565b505050929093029392505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203611c2857611c28611857565b506001019056fea164736f6c634300080f000a") - operatorFeeVaultDeploymentBytecode = common.FromHex("0x60e060405234801561001057600080fd5b5073420000000000000000000000000000000000001960a0526000608052600160c05260805160a05160c0516107ef6100a7600039600081816101b3015281816102450152818161044b015261048601526000818160b8015281816101800152818161039a01528181610429015281816104c201526105b70152600081816101ef01528181610279015261029d01526107ef6000f3fe60806040526004361061009a5760003560e01c806382356d8a1161006957806384411d651161004e57806384411d651461021d578063d0e12f9014610233578063d3e5792b1461026757600080fd5b806382356d8a146101a45780638312f149146101e057600080fd5b80630d9019e1146100a65780633ccfd60b1461010457806354fd4d501461011b57806366d003ac1461017157600080fd5b366100a157005b600080fd5b3480156100b257600080fd5b506100da7f000000000000000000000000000000000000000000000000000000000000000081565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020015b60405180910390f35b34801561011057600080fd5b5061011961029b565b005b34801561012757600080fd5b506101646040518060400160405280600c81526020017f312e352e302d626574612e35000000000000000000000000000000000000000081525081565b6040516100fb9190610671565b34801561017d57600080fd5b507f00000000000000000000000000000000000000000000000000000000000000006100da565b3480156101b057600080fd5b507f00000000000000000000000000000000000000000000000000000000000000005b6040516100fb919061074e565b3480156101ec57600080fd5b507f00000000000000000000000000000000000000000000000000000000000000005b6040519081526020016100fb565b34801561022957600080fd5b5061020f60005481565b34801561023f57600080fd5b506101d37f000000000000000000000000000000000000000000000000000000000000000081565b34801561027357600080fd5b5061020f7f000000000000000000000000000000000000000000000000000000000000000081565b7f0000000000000000000000000000000000000000000000000000000000000000471015610376576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604a60248201527f4665655661756c743a207769746864726177616c20616d6f756e74206d75737460448201527f2062652067726561746572207468616e206d696e696d756d207769746864726160648201527f77616c20616d6f756e7400000000000000000000000000000000000000000000608482015260a4015b60405180910390fd5b60004790508060008082825461038c9190610762565b9091555050604080518281527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff166020820152338183015290517fc8a211cc64b6ed1b50595a9fcb1932b6d1e5a6e8ef15b60e5b1f988ea9086bba9181900360600190a17f38e04cbeb8c10f8f568618aa75be0f10b6729b8b4237743b4de20cbcde2839ee817f0000000000000000000000000000000000000000000000000000000000000000337f000000000000000000000000000000000000000000000000000000000000000060405161047a94939291906107a1565b60405180910390a160017f000000000000000000000000000000000000000000000000000000000000000060018111156104b6576104b66106e4565b0361057a5760006104e77f000000000000000000000000000000000000000000000000000000000000000083610649565b905080610576576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603060248201527f4665655661756c743a206661696c656420746f2073656e642045544820746f2060448201527f4c322066656520726563697069656e7400000000000000000000000000000000606482015260840161036d565b5050565b6040517fc2b3e5ac00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016600482015262061a80602482015260606044820152600060648201527342000000000000000000000000000000000000169063c2b3e5ac9083906084016000604051808303818588803b15801561062d57600080fd5b505af1158015610641573d6000803e3d6000fd5b505050505050565b6000610656835a8461065d565b9392505050565b6000806000806000858888f1949350505050565b600060208083528351808285015260005b8181101561069e57858101830151858201604001528201610682565b818111156106b0576000604083870101525b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016929092016040019392505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b6002811061074a577f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b9052565b6020810161075c8284610713565b92915050565b6000821982111561079c577f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b500190565b84815273ffffffffffffffffffffffffffffffffffffffff848116602083015283166040820152608081016107d96060830184610713565b9594505050505056fea164736f6c634300080f000a") + l1BlockIsthmusDeploymentBytecode = common.FromHex("0x608060405234801561001057600080fd5b506106ae806100206000396000f3fe608060405234801561001057600080fd5b50600436106101825760003560e01c806364ca23ef116100d8578063b80777ea1161008c578063e591b28211610066578063e591b282146103b0578063e81b2c6d146103d2578063f8206140146103db57600080fd5b8063b80777ea14610337578063c598591814610357578063d84447151461037757600080fd5b80638381f58a116100bd5780638381f58a146103115780638b239f73146103255780639e8c49661461032e57600080fd5b806364ca23ef146102e157806368d5dca6146102f557600080fd5b80634397dfef1161013a57806354fd4d501161011457806354fd4d501461025d578063550fcdc91461029f5780635cf24969146102d857600080fd5b80634397dfef146101fc578063440a5e20146102245780634d5d9a2a1461022c57600080fd5b806309bd5a601161016b57806309bd5a60146101a457806316d3bc7f146101c057806321326849146101ed57600080fd5b8063015d8eb914610187578063098999be1461019c575b600080fd5b61019a6101953660046105bc565b6103e4565b005b61019a610523565b6101ad60025481565b6040519081526020015b60405180910390f35b6008546101d49067ffffffffffffffff1681565b60405167ffffffffffffffff90911681526020016101b7565b604051600081526020016101b7565b6040805173eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee815260126020820152016101b7565b61019a61052d565b6008546102489068010000000000000000900463ffffffff1681565b60405163ffffffff90911681526020016101b7565b60408051808201909152600581527f312e362e3000000000000000000000000000000000000000000000000000000060208201525b6040516101b7919061062e565b60408051808201909152600381527f45544800000000000000000000000000000000000000000000000000000000006020820152610292565b6101ad60015481565b6003546101d49067ffffffffffffffff1681565b6003546102489068010000000000000000900463ffffffff1681565b6000546101d49067ffffffffffffffff1681565b6101ad60055481565b6101ad60065481565b6000546101d49068010000000000000000900467ffffffffffffffff1681565b600354610248906c01000000000000000000000000900463ffffffff1681565b60408051808201909152600581527f45746865720000000000000000000000000000000000000000000000000000006020820152610292565b60405173deaddeaddeaddeaddeaddeaddeaddeaddead000181526020016101b7565b6101ad60045481565b6101ad60075481565b3373deaddeaddeaddeaddeaddeaddeaddeaddead00011461048b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603b60248201527f4c31426c6f636b3a206f6e6c7920746865206465706f7369746f72206163636f60448201527f756e742063616e20736574204c3120626c6f636b2076616c7565730000000000606482015260840160405180910390fd5b6000805467ffffffffffffffff98891668010000000000000000027fffffffffffffffffffffffffffffffff00000000000000000000000000000000909116998916999099179890981790975560019490945560029290925560038054919094167fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000009190911617909255600491909155600555600655565b61052b610535565b565b61052b610548565b61053d610548565b60a43560a01c600855565b73deaddeaddeaddeaddeaddeaddeaddeaddead000133811461057257633cc50b456000526004601cfd5b60043560801c60035560143560801c60005560243560015560443560075560643560025560843560045550565b803567ffffffffffffffff811681146105b757600080fd5b919050565b600080600080600080600080610100898b0312156105d957600080fd5b6105e28961059f565b97506105f060208a0161059f565b9650604089013595506060890135945061060c60808a0161059f565b979a969950949793969560a0850135955060c08501359460e001359350915050565b600060208083528351808285015260005b8181101561065b5785810183015185820160400152820161063f565b8181111561066d576000604083870101525b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01692909201604001939250505056fea164736f6c634300080f000a") + gasPriceOracleIsthmusDeploymentBytecode = common.FromHex("0x608060405234801561001057600080fd5b50611c3c806100206000396000f3fe608060405234801561001057600080fd5b50600436106101775760003560e01c806368d5dca6116100d8578063c59859181161008c578063f45e65d811610066578063f45e65d8146102ca578063f8206140146102d2578063fe173b971461026957600080fd5b8063c59859181461029c578063de26c4a1146102a4578063f1c7a58b146102b757600080fd5b80638e98b106116100bd5780638e98b1061461026f578063960e3a2314610277578063b54501bc1461028957600080fd5b806368d5dca61461024c5780636ef25c3a1461026957600080fd5b8063313ce5671161012f5780634ef6e224116101145780634ef6e224146101de578063519b4bd3146101fb57806354fd4d501461020357600080fd5b8063313ce567146101c457806349948e0e146101cb57600080fd5b8063275aedd211610160578063275aedd2146101a1578063291b0383146101b45780632e0f2625146101bc57600080fd5b80630c18c1621461017c57806322b90ab314610197575b600080fd5b6101846102da565b6040519081526020015b60405180910390f35b61019f6103fb565b005b6101846101af36600461168e565b610584565b61019f61070f565b610184600681565b6006610184565b6101846101d93660046116d6565b610937565b6000546101eb9060ff1681565b604051901515815260200161018e565b61018461096e565b61023f6040518060400160405280600581526020017f312e342e3000000000000000000000000000000000000000000000000000000081525081565b60405161018e91906117a5565b6102546109cf565b60405163ffffffff909116815260200161018e565b48610184565b61019f610a54565b6000546101eb90610100900460ff1681565b6000546101eb9062010000900460ff1681565b610254610c4e565b6101846102b23660046116d6565b610caf565b6101846102c536600461168e565b610da9565b610184610e85565b610184610f78565b6000805460ff1615610373576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602860248201527f47617350726963654f7261636c653a206f76657268656164282920697320646560448201527f707265636174656400000000000000000000000000000000000000000000000060648201526084015b60405180910390fd5b73420000000000000000000000000000000000001573ffffffffffffffffffffffffffffffffffffffff16638b239f736040518163ffffffff1660e01b8152600401602060405180830381865afa1580156103d2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103f69190611818565b905090565b3373deaddeaddeaddeaddeaddeaddeaddeaddead0001146104c4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604160248201527f47617350726963654f7261636c653a206f6e6c7920746865206465706f73697460448201527f6f72206163636f756e742063616e2073657420697345636f746f6e6520666c6160648201527f6700000000000000000000000000000000000000000000000000000000000000608482015260a40161036a565b60005460ff1615610557576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f47617350726963654f7261636c653a2045636f746f6e6520616c72656164792060448201527f6163746976650000000000000000000000000000000000000000000000000000606482015260840161036a565b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055565b6000805462010000900460ff1661059d57506000919050565b610709620f42406106668473420000000000000000000000000000000000001573ffffffffffffffffffffffffffffffffffffffff16634d5d9a2a6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610607573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061062b9190611831565b63ffffffff167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff821583830293840490921491909117011790565b6106709190611886565b73420000000000000000000000000000000000001573ffffffffffffffffffffffffffffffffffffffff166316d3bc7f6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156106cf573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106f391906118c1565b67ffffffffffffffff1681019081106000031790565b92915050565b3373deaddeaddeaddeaddeaddeaddeaddeaddead0001146107d8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604160248201527f47617350726963654f7261636c653a206f6e6c7920746865206465706f73697460448201527f6f72206163636f756e742063616e20736574206973497374686d757320666c6160648201527f6700000000000000000000000000000000000000000000000000000000000000608482015260a40161036a565b600054610100900460ff1661086f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603960248201527f47617350726963654f7261636c653a20497374686d75732063616e206f6e6c7960448201527f2062652061637469766174656420616674657220466a6f726400000000000000606482015260840161036a565b60005462010000900460ff1615610908576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f47617350726963654f7261636c653a20497374686d757320616c72656164792060448201527f6163746976650000000000000000000000000000000000000000000000000000606482015260840161036a565b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffff1662010000179055565b60008054610100900460ff16156109515761070982610fd9565b60005460ff16156109655761070982610ff8565b6107098261109c565b600073420000000000000000000000000000000000001573ffffffffffffffffffffffffffffffffffffffff16635cf249696040518163ffffffff1660e01b8152600401602060405180830381865afa1580156103d2573d6000803e3d6000fd5b600073420000000000000000000000000000000000001573ffffffffffffffffffffffffffffffffffffffff166368d5dca66040518163ffffffff1660e01b8152600401602060405180830381865afa158015610a30573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103f69190611831565b3373deaddeaddeaddeaddeaddeaddeaddeaddead000114610af7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603f60248201527f47617350726963654f7261636c653a206f6e6c7920746865206465706f73697460448201527f6f72206163636f756e742063616e20736574206973466a6f726420666c616700606482015260840161036a565b60005460ff16610b89576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603960248201527f47617350726963654f7261636c653a20466a6f72642063616e206f6e6c79206260448201527f65206163746976617465642061667465722045636f746f6e6500000000000000606482015260840161036a565b600054610100900460ff1615610c20576040517f08c379a0000000000000000000000000000000000000000000000000000000008152602060048201526024808201527f47617350726963654f7261636c653a20466a6f726420616c726561647920616360448201527f7469766500000000000000000000000000000000000000000000000000000000606482015260840161036a565b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff16610100179055565b600073420000000000000000000000000000000000001573ffffffffffffffffffffffffffffffffffffffff1663c59859186040518163ffffffff1660e01b8152600401602060405180830381865afa158015610a30573d6000803e3d6000fd5b60008054610100900460ff1615610cf657620f4240610ce1610cd0846111f0565b51610cdc9060446118eb565b61150d565b610cec906010611903565b6107099190611886565b6000610d018361156c565b60005490915060ff1615610d155792915050565b73420000000000000000000000000000000000001573ffffffffffffffffffffffffffffffffffffffff16638b239f736040518163ffffffff1660e01b8152600401602060405180830381865afa158015610d74573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d989190611818565b610da290826118eb565b9392505050565b60008054610100900460ff16610e41576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603660248201527f47617350726963654f7261636c653a206765744c314665655570706572426f7560448201527f6e64206f6e6c7920737570706f72747320466a6f726400000000000000000000606482015260840161036a565b6000610e4e8360446118eb565b90506000610e5d60ff83611886565b610e6790836118eb565b610e729060106118eb565b9050610e7d816115fc565b949350505050565b6000805460ff1615610f19576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f47617350726963654f7261636c653a207363616c61722829206973206465707260448201527f6563617465640000000000000000000000000000000000000000000000000000606482015260840161036a565b73420000000000000000000000000000000000001573ffffffffffffffffffffffffffffffffffffffff16639e8c49666040518163ffffffff1660e01b8152600401602060405180830381865afa1580156103d2573d6000803e3d6000fd5b600073420000000000000000000000000000000000001573ffffffffffffffffffffffffffffffffffffffff1663f82061406040518163ffffffff1660e01b8152600401602060405180830381865afa1580156103d2573d6000803e3d6000fd5b6000610709610fe7836111f0565b51610ff39060446118eb565b6115fc565b6000806110048361156c565b9050600061101061096e565b611018610c4e565b611023906010611940565b63ffffffff166110339190611903565b9050600061103f610f78565b6110476109cf565b63ffffffff166110579190611903565b9050600061106582846118eb565b61106f9085611903565b905061107d6006600a611a8c565b611088906010611903565b6110929082611886565b9695505050505050565b6000806110a88361156c565b9050600073420000000000000000000000000000000000001573ffffffffffffffffffffffffffffffffffffffff16639e8c49666040518163ffffffff1660e01b8152600401602060405180830381865afa15801561110b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061112f9190611818565b61113761096e565b73420000000000000000000000000000000000001573ffffffffffffffffffffffffffffffffffffffff16638b239f736040518163ffffffff1660e01b8152600401602060405180830381865afa158015611196573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111ba9190611818565b6111c490856118eb565b6111ce9190611903565b6111d89190611903565b90506111e66006600a611a8c565b610e7d9082611886565b606061137f565b818153600101919050565b600082840393505b83811015610da25782810151828201511860001a159093029260010161120a565b825b60208210611277578251611242601f836111f7565b52602092909201917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09091019060210161122d565b8115610da257825161128c60018403836111f7565b520160010192915050565b60006001830392505b61010782106112d8576112ca8360ff166112c560fd6112c58760081c60e001896111f7565b6111f7565b9350610106820391506112a0565b60078210611305576112fe8360ff166112c5600785036112c58760081c60e001896111f7565b9050610da2565b610e7d8360ff166112c58560081c8560051b01876111f7565b61137782820361135b61134b84600081518060001a8160011a60081b178160021a60101b17915050919050565b639e3779b90260131c611fff1690565b8060021b6040510182815160e01c1860e01b8151188152505050565b600101919050565b6180003860405139618000604051016020830180600d8551820103826002015b818110156114b2576000805b50508051604051600082901a600183901a60081b1760029290921a60101b91909117639e3779b9810260111c617ffc16909101805160e081811c878603811890911b909118909152840190818303908484106114075750611442565b600184019350611fff821161143c578251600081901a600182901a60081b1760029190911a60101b17810361143c5750611442565b506113ab565b8383106114505750506114b2565b6001830392508583111561146e5761146b878788860361122b565b96505b611482600985016003850160038501611202565b915061148f878284611297565b9650506114a7846114a28684860161131e565b61131e565b91505080935061139f565b50506114c4838384885185010361122b565b925050506040519150618000820180820391508183526020830160005b838110156114f95782810151828201526020016114e1565b506000920191825250602001604052919050565b60008061151d83620cc394611903565b611547907ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd763200611a98565b90506115576064620f4240611b0c565b81121561070957610da26064620f4240611b0c565b80516000908190815b818110156115ef5784818151811061158f5761158f611bc8565b01602001517fff00000000000000000000000000000000000000000000000000000000000000166000036115cf576115c86004846118eb565b92506115dd565b6115da6010846118eb565b92505b806115e781611bf7565b915050611575565b50610e7d826104406118eb565b6000806116088361150d565b90506000611614610f78565b61161c6109cf565b63ffffffff1661162c9190611903565b61163461096e565b61163c610c4e565b611647906010611940565b63ffffffff166116579190611903565b61166191906118eb565b905061166f60066002611903565b61167a90600a611a8c565b6116848284611903565b610e7d9190611886565b6000602082840312156116a057600080fd5b5035919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6000602082840312156116e857600080fd5b813567ffffffffffffffff8082111561170057600080fd5b818401915084601f83011261171457600080fd5b813581811115611726576117266116a7565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f0116810190838211818310171561176c5761176c6116a7565b8160405282815287602084870101111561178557600080fd5b826020860160208301376000928101602001929092525095945050505050565b600060208083528351808285015260005b818110156117d2578581018301518582016040015282016117b6565b818111156117e4576000604083870101525b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016929092016040019392505050565b60006020828403121561182a57600080fd5b5051919050565b60006020828403121561184357600080fd5b815163ffffffff81168114610da257600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000826118bc577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b6000602082840312156118d357600080fd5b815167ffffffffffffffff81168114610da257600080fd5b600082198211156118fe576118fe611857565b500190565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048311821515161561193b5761193b611857565b500290565b600063ffffffff8083168185168183048111821515161561196357611963611857565b02949350505050565b600181815b808511156119c557817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048211156119ab576119ab611857565b808516156119b857918102915b93841c9390800290611971565b509250929050565b6000826119dc57506001610709565b816119e957506000610709565b81600181146119ff5760028114611a0957611a25565b6001915050610709565b60ff841115611a1a57611a1a611857565b50506001821b610709565b5060208310610133831016604e8410600b8410161715611a48575081810a610709565b611a52838361196c565b807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04821115611a8457611a84611857565b029392505050565b6000610da283836119cd565b6000808212827f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff03841381151615611ad257611ad2611857565b827f8000000000000000000000000000000000000000000000000000000000000000038412811615611b0657611b06611857565b50500190565b60007f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600084136000841385830485118282161615611b4d57611b4d611857565b7f80000000000000000000000000000000000000000000000000000000000000006000871286820588128184161615611b8857611b88611857565b60008712925087820587128484161615611ba457611ba4611857565b87850587128184161615611bba57611bba611857565b505050929093029392505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203611c2857611c28611857565b506001019056fea164736f6c634300080f000a") + operatorFeeVaultDeploymentBytecode = common.FromHex("0x60e060405234801561001057600080fd5b5073420000000000000000000000000000000000001960a0526000608052600160c05260805160a05160c0516107ef6100a7600039600081816101b3015281816102450152818161044b015261048601526000818160b8015281816101800152818161039a01528181610429015281816104c201526105b70152600081816101ef01528181610279015261029d01526107ef6000f3fe60806040526004361061009a5760003560e01c806382356d8a1161006957806384411d651161004e57806384411d651461021d578063d0e12f9014610233578063d3e5792b1461026757600080fd5b806382356d8a146101a45780638312f149146101e057600080fd5b80630d9019e1146100a65780633ccfd60b1461010457806354fd4d501461011b57806366d003ac1461017157600080fd5b366100a157005b600080fd5b3480156100b257600080fd5b506100da7f000000000000000000000000000000000000000000000000000000000000000081565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020015b60405180910390f35b34801561011057600080fd5b5061011961029b565b005b34801561012757600080fd5b506101646040518060400160405280600581526020017f312e302e3000000000000000000000000000000000000000000000000000000081525081565b6040516100fb9190610671565b34801561017d57600080fd5b507f00000000000000000000000000000000000000000000000000000000000000006100da565b3480156101b057600080fd5b507f00000000000000000000000000000000000000000000000000000000000000005b6040516100fb919061074e565b3480156101ec57600080fd5b507f00000000000000000000000000000000000000000000000000000000000000005b6040519081526020016100fb565b34801561022957600080fd5b5061020f60005481565b34801561023f57600080fd5b506101d37f000000000000000000000000000000000000000000000000000000000000000081565b34801561027357600080fd5b5061020f7f000000000000000000000000000000000000000000000000000000000000000081565b7f0000000000000000000000000000000000000000000000000000000000000000471015610376576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604a60248201527f4665655661756c743a207769746864726177616c20616d6f756e74206d75737460448201527f2062652067726561746572207468616e206d696e696d756d207769746864726160648201527f77616c20616d6f756e7400000000000000000000000000000000000000000000608482015260a4015b60405180910390fd5b60004790508060008082825461038c9190610762565b9091555050604080518281527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff166020820152338183015290517fc8a211cc64b6ed1b50595a9fcb1932b6d1e5a6e8ef15b60e5b1f988ea9086bba9181900360600190a17f38e04cbeb8c10f8f568618aa75be0f10b6729b8b4237743b4de20cbcde2839ee817f0000000000000000000000000000000000000000000000000000000000000000337f000000000000000000000000000000000000000000000000000000000000000060405161047a94939291906107a1565b60405180910390a160017f000000000000000000000000000000000000000000000000000000000000000060018111156104b6576104b66106e4565b0361057a5760006104e77f000000000000000000000000000000000000000000000000000000000000000083610649565b905080610576576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603060248201527f4665655661756c743a206661696c656420746f2073656e642045544820746f2060448201527f4c322066656520726563697069656e7400000000000000000000000000000000606482015260840161036d565b5050565b6040517fc2b3e5ac00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016600482015262061a80602482015260606044820152600060648201527342000000000000000000000000000000000000169063c2b3e5ac9083906084016000604051808303818588803b15801561062d57600080fd5b505af1158015610641573d6000803e3d6000fd5b505050505050565b6000610656835a8461065d565b9392505050565b6000806000806000858888f1949350505050565b600060208083528351808285015260005b8181101561069e57858101830151858201604001528201610682565b818111156106b0576000604083870101525b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016929092016040019392505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b6002811061074a577f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b9052565b6020810161075c8284610713565b92915050565b6000821982111561079c577f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b500190565b84815273ffffffffffffffffffffffffffffffffffffffff848116602083015283166040820152608081016107d96060830184610713565b9594505050505056fea164736f6c634300080f000a") // Enable Isthmus Parameters enableIsthmusSource = UpgradeDepositSource{Intent: "Isthmus: Gas Price Oracle Set Isthmus"} diff --git a/op-service/sources/eth_client.go b/op-service/sources/eth_client.go index 0008b05355d..48800012b29 100644 --- a/op-service/sources/eth_client.go +++ b/op-service/sources/eth_client.go @@ -69,6 +69,25 @@ type EthClientConfig struct { MethodResetDuration time.Duration } +// DefaultEthClientConfig creates a new eth client config, +// with caching of data using the given cache-size (in number of blocks). +func DefaultEthClientConfig(cacheSize int) *EthClientConfig { + return &EthClientConfig{ + // receipts and transactions are cached per block + ReceiptsCacheSize: cacheSize, + TransactionsCacheSize: cacheSize, + HeadersCacheSize: cacheSize, + PayloadsCacheSize: cacheSize, + MaxRequestsPerBatch: 20, + MaxConcurrentRequests: 10, + BlockRefsCacheSize: cacheSize, + TrustRPC: false, + MustBePostMerge: true, + RPCProviderKind: RPCKindStandard, + MethodResetDuration: time.Minute, + } +} + func (c *EthClientConfig) Check() error { if c.ReceiptsCacheSize < 0 { return fmt.Errorf("invalid receipts cache size: %d", c.ReceiptsCacheSize) diff --git a/packages/contracts-bedrock/interfaces/L1/IETHLockbox.sol b/packages/contracts-bedrock/interfaces/L1/IETHLockbox.sol index 5d4abd72c79..7f9f5c2d0d1 100644 --- a/packages/contracts-bedrock/interfaces/L1/IETHLockbox.sol +++ b/packages/contracts-bedrock/interfaces/L1/IETHLockbox.sol @@ -12,20 +12,21 @@ interface IETHLockbox is IProxyAdminOwnedBase, ISemver { error ETHLockbox_InsufficientBalance(); error ETHLockbox_NoWithdrawalTransactions(); error ETHLockbox_DifferentProxyAdminOwner(); + error ETHLockbox_DifferentSuperchainConfig(); event Initialized(uint8 version); - event ETHLocked(address indexed portal, uint256 amount); - event ETHUnlocked(address indexed portal, uint256 amount); - event PortalAuthorized(address indexed portal); - event LockboxAuthorized(address indexed lockbox); - event LiquidityMigrated(address indexed lockbox, uint256 amount); - event LiquidityReceived(address indexed lockbox, uint256 amount); + event ETHLocked(IOptimismPortal2 indexed portal, uint256 amount); + event ETHUnlocked(IOptimismPortal2 indexed portal, uint256 amount); + event PortalAuthorized(IOptimismPortal2 indexed portal); + event LockboxAuthorized(IETHLockbox indexed lockbox); + event LiquidityMigrated(IETHLockbox indexed lockbox, uint256 amount); + event LiquidityReceived(IETHLockbox indexed lockbox, uint256 amount); function initialize(ISuperchainConfig _superchainConfig, IOptimismPortal2[] calldata _portals) external; function superchainConfig() external view returns (ISuperchainConfig); function paused() external view returns (bool); - function authorizedPortals(address) external view returns (bool); - function authorizedLockboxes(address) external view returns (bool); + function authorizedPortals(IOptimismPortal2) external view returns (bool); + function authorizedLockboxes(IETHLockbox) external view returns (bool); function receiveLiquidity() external payable; function lockETH() external payable; function unlockETH(uint256 _value) external; diff --git a/packages/contracts-bedrock/interfaces/L1/IOPContractsManager.sol b/packages/contracts-bedrock/interfaces/L1/IOPContractsManager.sol index d2f83d685d9..09821208687 100644 --- a/packages/contracts-bedrock/interfaces/L1/IOPContractsManager.sol +++ b/packages/contracts-bedrock/interfaces/L1/IOPContractsManager.sol @@ -108,7 +108,6 @@ interface IOPContractsManager { string saltMixer; uint64 gasLimit; // Configurable dispute game parameters. - bool disputeGameUsesSuperRoots; GameType disputeGameType; Claim disputeAbsolutePrestate; uint256 disputeMaxGameDepth; @@ -176,7 +175,6 @@ interface IOPContractsManager { ISystemConfig systemConfigProxy; IProxyAdmin proxyAdmin; Claim absolutePrestate; - bool disputeGameUsesSuperRoots; } struct AddGameInput { diff --git a/packages/contracts-bedrock/interfaces/L1/IOPContractsManagerLegacyUpgrade.sol b/packages/contracts-bedrock/interfaces/L1/IOPContractsManagerLegacyUpgrade.sol deleted file mode 100644 index 057139005a9..00000000000 --- a/packages/contracts-bedrock/interfaces/L1/IOPContractsManagerLegacyUpgrade.sol +++ /dev/null @@ -1,21 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; - -import { ISystemConfig } from "interfaces/L1/ISystemConfig.sol"; -import { IProxyAdmin } from "interfaces/universal/IProxyAdmin.sol"; -import { Claim } from "src/dispute/lib/Types.sol"; - -/// @title IOPContractsManagerLegacyUpgrade -/// @notice Interface for the legacy OPContractsManager upgrade function. -/// This interface is used to test Upgrade 13 and 14 paths and can be safely removed -/// after those upgrades are completed. Only difference in the new struct is the added -/// disputeGameUsesSuperRoots boolean. -interface IOPContractsManagerLegacyUpgrade { - struct OpChainConfig { - ISystemConfig systemConfigProxy; - IProxyAdmin proxyAdmin; - Claim absolutePrestate; - } - - function upgrade(OpChainConfig[] memory _opChainConfigs) external; -} diff --git a/packages/contracts-bedrock/interfaces/L1/IOptimismPortal2.sol b/packages/contracts-bedrock/interfaces/L1/IOptimismPortal2.sol index f9cfc532543..673b3c778f8 100644 --- a/packages/contracts-bedrock/interfaces/L1/IOptimismPortal2.sol +++ b/packages/contracts-bedrock/interfaces/L1/IOptimismPortal2.sol @@ -37,6 +37,7 @@ interface IOptimismPortal2 is IProxyAdminOwnedBase { error OptimismPortal_InvalidSuperRootProof(); error OptimismPortal_InvalidOutputRootChainId(); error OptimismPortal_WrongProofMethod(); + error OptimismPortal_MigratingToSameRegistry(); error Encoding_EmptySuperRoot(); error Encoding_InvalidSuperRootVersion(); error OutOfGas(); @@ -49,7 +50,7 @@ interface IOptimismPortal2 is IProxyAdminOwnedBase { event WithdrawalProven(bytes32 indexed withdrawalHash, address indexed from, address indexed to); event WithdrawalProvenExtension1(bytes32 indexed withdrawalHash, address indexed proofSubmitter); event ETHMigrated(address indexed lockbox, uint256 ethBalance); - event LockboxUpdated(address oldLockbox, address newLockbox); + event PortalMigrated(IETHLockbox oldLockbox, IETHLockbox newLockbox, IAnchorStateRegistry oldAnchorStateRegistry, IAnchorStateRegistry newAnchorStateRegistry); receive() external payable; @@ -68,7 +69,7 @@ interface IOptimismPortal2 is IProxyAdminOwnedBase { function disputeGameFactory() external view returns (IDisputeGameFactory); function disputeGameFinalityDelaySeconds() external view returns (uint256); function donateETH() external payable; - function updateLockbox(IETHLockbox _newLockbox) external; + function migrateToSuperRoots(IETHLockbox _newLockbox, IAnchorStateRegistry _newAnchorStateRegistry) external; function finalizeWithdrawalTransaction(Types.WithdrawalTransaction memory _tx) external; function finalizeWithdrawalTransactionExternalProof( Types.WithdrawalTransaction memory _tx, @@ -81,8 +82,7 @@ interface IOptimismPortal2 is IProxyAdminOwnedBase { ISystemConfig _systemConfig, ISuperchainConfig _superchainConfig, IAnchorStateRegistry _anchorStateRegistry, - IETHLockbox _ethLockbox, - bool _superRootsActive + IETHLockbox _ethLockbox ) external; function initVersion() external view returns (uint8); @@ -123,8 +123,7 @@ interface IOptimismPortal2 is IProxyAdminOwnedBase { function systemConfig() external view returns (ISystemConfig); function upgrade( IAnchorStateRegistry _anchorStateRegistry, - IETHLockbox _ethLockbox, - bool _superRootsActive + IETHLockbox _ethLockbox ) external; function version() external pure returns (string memory); diff --git a/packages/contracts-bedrock/interfaces/L2/IL2ToL2CrossDomainMessenger.sol b/packages/contracts-bedrock/interfaces/L2/IL2ToL2CrossDomainMessenger.sol index d1bfdf57f9a..9bc158d46ca 100644 --- a/packages/contracts-bedrock/interfaces/L2/IL2ToL2CrossDomainMessenger.sol +++ b/packages/contracts-bedrock/interfaces/L2/IL2ToL2CrossDomainMessenger.sol @@ -36,9 +36,6 @@ interface IL2ToL2CrossDomainMessenger { /// @notice Thrown when a reentrant call is detected. error ReentrantCall(); - /// @notice Thrown when a call to the target contract during message relay fails. - error TargetCallFailed(); - /// @notice Emitted whenever a message is sent to a destination /// @param destination Chain ID of the destination chain. /// @param target Target contract or wallet address. diff --git a/packages/contracts-bedrock/scripts/deploy/ChainAssertions.sol b/packages/contracts-bedrock/scripts/deploy/ChainAssertions.sol index 92972355fa4..2b6d4d4597e 100644 --- a/packages/contracts-bedrock/scripts/deploy/ChainAssertions.sol +++ b/packages/contracts-bedrock/scripts/deploy/ChainAssertions.sol @@ -396,11 +396,14 @@ library ChainAssertions { if (_isProxy) { require(ethLockbox.superchainConfig() == superchainConfig, "CHECK-ELB-20"); - require(ethLockbox.authorizedPortals(_contracts.OptimismPortal), "CHECK-ELB-30"); + require(ethLockbox.authorizedPortals(IOptimismPortal(payable(_contracts.OptimismPortal))), "CHECK-ELB-30"); require(ethLockbox.proxyAdminOwner() == _cfg.finalSystemOwner(), "CHECK-ELB-40"); } else { require(address(ethLockbox.superchainConfig()) == address(0), "CHECK-ELB-50"); - require(ethLockbox.authorizedPortals(_contracts.OptimismPortal) == false, "CHECK-ELB-60"); + require( + ethLockbox.authorizedPortals(IOptimismPortal(payable(_contracts.OptimismPortal))) == false, + "CHECK-ELB-60" + ); } } diff --git a/packages/contracts-bedrock/scripts/deploy/Deploy.s.sol b/packages/contracts-bedrock/scripts/deploy/Deploy.s.sol index 3d7f889086e..10f11cc9594 100644 --- a/packages/contracts-bedrock/scripts/deploy/Deploy.s.sol +++ b/packages/contracts-bedrock/scripts/deploy/Deploy.s.sol @@ -961,7 +961,6 @@ contract Deploy is Deployer { ), saltMixer: saltMixer, gasLimit: uint64(cfg.l2GenesisBlockGasLimit()), - disputeGameUsesSuperRoots: false, disputeGameType: GameTypes.PERMISSIONED_CANNON, disputeAbsolutePrestate: Claim.wrap(bytes32(cfg.faultGameAbsolutePrestate())), disputeMaxGameDepth: cfg.faultGameMaxDepth(), diff --git a/packages/contracts-bedrock/scripts/deploy/DeployImplementations.s.sol b/packages/contracts-bedrock/scripts/deploy/DeployImplementations.s.sol index 2666ac296a8..1035827375a 100644 --- a/packages/contracts-bedrock/scripts/deploy/DeployImplementations.s.sol +++ b/packages/contracts-bedrock/scripts/deploy/DeployImplementations.s.sol @@ -369,7 +369,7 @@ contract DeployImplementationsOutput is BaseDeployIO { DeployUtils.assertInitialized({ _contractAddress: address(lockbox), _isProxy: false, _slot: 0, _offset: 0 }); require(address(lockbox.superchainConfig()) == address(0), "ELB-10"); - require(lockbox.authorizedPortals(address(optimismPortalImpl())) == false, "ELB-20"); + require(lockbox.authorizedPortals(optimismPortalImpl()) == false, "ELB-20"); } function assertValidDelayedWETHImpl(DeployImplementationsInput _dii) internal view { diff --git a/packages/contracts-bedrock/scripts/deploy/DeployOPChain.s.sol b/packages/contracts-bedrock/scripts/deploy/DeployOPChain.s.sol index 2978582420a..042f06a2a02 100644 --- a/packages/contracts-bedrock/scripts/deploy/DeployOPChain.s.sol +++ b/packages/contracts-bedrock/scripts/deploy/DeployOPChain.s.sol @@ -52,7 +52,6 @@ contract DeployOPChainInput is BaseDeployIO { uint64 internal _gasLimit; // Configurable dispute game inputs - bool internal _disputeGameUsesSuperRoots; GameType internal _disputeGameType; Claim internal _disputeAbsolutePrestate; uint256 internal _disputeMaxGameDepth; @@ -100,9 +99,6 @@ contract DeployOPChainInput is BaseDeployIO { _operatorFeeScalar = SafeCast.toUint32(_value); } else if (_sel == this.operatorFeeConstant.selector) { _operatorFeeConstant = SafeCast.toUint64(_value); - } else if (_sel == this.disputeGameUsesSuperRoots.selector) { - require(_value == 0 || _value == 1, "DeployOPChainInput: invalid disputeGameUsesSuperRoots"); - _disputeGameUsesSuperRoots = _value == 1; } else { revert("DeployOPChainInput: unknown selector"); } @@ -199,10 +195,6 @@ contract DeployOPChainInput is BaseDeployIO { return _gasLimit; } - function disputeGameUsesSuperRoots() public view returns (bool) { - return _disputeGameUsesSuperRoots; - } - function disputeGameType() public view returns (GameType) { return _disputeGameType; } @@ -388,7 +380,6 @@ contract DeployOPChain is Script { startingAnchorRoot: _doi.startingAnchorRoot(), saltMixer: _doi.saltMixer(), gasLimit: _doi.gasLimit(), - disputeGameUsesSuperRoots: _doi.disputeGameUsesSuperRoots(), disputeGameType: _doi.disputeGameType(), disputeAbsolutePrestate: _doi.disputeAbsolutePrestate(), disputeMaxGameDepth: _doi.disputeMaxGameDepth(), @@ -655,7 +646,7 @@ contract DeployOPChain is Script { IETHLockbox lockbox = _doo.ethLockboxProxy(); require(address(lockbox.superchainConfig()) == address(_doi.opcm().superchainConfig()), "ETHLOCKBOX-10"); - require(lockbox.authorizedPortals(address(_doo.optimismPortalProxy())), "ETHLOCKBOX-20"); + require(lockbox.authorizedPortals(_doo.optimismPortalProxy()), "ETHLOCKBOX-20"); require(lockbox.proxyAdminOwner() == _doi.opChainProxyAdminOwner(), "ETHLOCKBOX-30"); } diff --git a/packages/contracts-bedrock/snapshots/abi/ETHLockbox.json b/packages/contracts-bedrock/snapshots/abi/ETHLockbox.json index d0e9c8fb9a6..50b2a344f05 100644 --- a/packages/contracts-bedrock/snapshots/abi/ETHLockbox.json +++ b/packages/contracts-bedrock/snapshots/abi/ETHLockbox.json @@ -33,7 +33,7 @@ { "inputs": [ { - "internalType": "address", + "internalType": "contract IETHLockbox", "name": "", "type": "address" } @@ -52,7 +52,7 @@ { "inputs": [ { - "internalType": "address", + "internalType": "contract IOptimismPortal2", "name": "", "type": "address" } @@ -183,7 +183,7 @@ "inputs": [ { "indexed": true, - "internalType": "address", + "internalType": "contract IOptimismPortal2", "name": "portal", "type": "address" }, @@ -202,7 +202,7 @@ "inputs": [ { "indexed": true, - "internalType": "address", + "internalType": "contract IOptimismPortal2", "name": "portal", "type": "address" }, @@ -234,7 +234,7 @@ "inputs": [ { "indexed": true, - "internalType": "address", + "internalType": "contract IETHLockbox", "name": "lockbox", "type": "address" }, @@ -253,7 +253,7 @@ "inputs": [ { "indexed": true, - "internalType": "address", + "internalType": "contract IETHLockbox", "name": "lockbox", "type": "address" }, @@ -272,7 +272,7 @@ "inputs": [ { "indexed": true, - "internalType": "address", + "internalType": "contract IETHLockbox", "name": "lockbox", "type": "address" } @@ -285,7 +285,7 @@ "inputs": [ { "indexed": true, - "internalType": "address", + "internalType": "contract IOptimismPortal2", "name": "portal", "type": "address" } @@ -298,6 +298,11 @@ "name": "ETHLockbox_DifferentProxyAdminOwner", "type": "error" }, + { + "inputs": [], + "name": "ETHLockbox_DifferentSuperchainConfig", + "type": "error" + }, { "inputs": [], "name": "ETHLockbox_InsufficientBalance", diff --git a/packages/contracts-bedrock/snapshots/abi/L2ToL2CrossDomainMessenger.json b/packages/contracts-bedrock/snapshots/abi/L2ToL2CrossDomainMessenger.json index 099e3267747..e54b24edf56 100644 --- a/packages/contracts-bedrock/snapshots/abi/L2ToL2CrossDomainMessenger.json +++ b/packages/contracts-bedrock/snapshots/abi/L2ToL2CrossDomainMessenger.json @@ -282,10 +282,5 @@ "inputs": [], "name": "ReentrantCall", "type": "error" - }, - { - "inputs": [], - "name": "TargetCallFailed", - "type": "error" } ] \ No newline at end of file diff --git a/packages/contracts-bedrock/snapshots/abi/OPContractsManager.json b/packages/contracts-bedrock/snapshots/abi/OPContractsManager.json index 381040d946e..47e5d823e8c 100644 --- a/packages/contracts-bedrock/snapshots/abi/OPContractsManager.json +++ b/packages/contracts-bedrock/snapshots/abi/OPContractsManager.json @@ -293,11 +293,6 @@ "name": "gasLimit", "type": "uint64" }, - { - "internalType": "bool", - "name": "disputeGameUsesSuperRoots", - "type": "bool" - }, { "internalType": "GameType", "name": "disputeGameType", @@ -637,11 +632,6 @@ "internalType": "Claim", "name": "absolutePrestate", "type": "bytes32" - }, - { - "internalType": "bool", - "name": "disputeGameUsesSuperRoots", - "type": "bool" } ], "internalType": "struct OPContractsManager.OpChainConfig[]", @@ -672,11 +662,6 @@ "internalType": "Claim", "name": "absolutePrestate", "type": "bytes32" - }, - { - "internalType": "bool", - "name": "disputeGameUsesSuperRoots", - "type": "bool" } ], "internalType": "struct OPContractsManager.OpChainConfig[]", diff --git a/packages/contracts-bedrock/snapshots/abi/OPContractsManagerDeployer.json b/packages/contracts-bedrock/snapshots/abi/OPContractsManagerDeployer.json index a3e0783fd0d..b24342213aa 100644 --- a/packages/contracts-bedrock/snapshots/abi/OPContractsManagerDeployer.json +++ b/packages/contracts-bedrock/snapshots/abi/OPContractsManagerDeployer.json @@ -186,11 +186,6 @@ "name": "gasLimit", "type": "uint64" }, - { - "internalType": "bool", - "name": "disputeGameUsesSuperRoots", - "type": "bool" - }, { "internalType": "GameType", "name": "disputeGameType", diff --git a/packages/contracts-bedrock/snapshots/abi/OPContractsManagerGameTypeAdder.json b/packages/contracts-bedrock/snapshots/abi/OPContractsManagerGameTypeAdder.json index 75a4caf4046..bc3cd926920 100644 --- a/packages/contracts-bedrock/snapshots/abi/OPContractsManagerGameTypeAdder.json +++ b/packages/contracts-bedrock/snapshots/abi/OPContractsManagerGameTypeAdder.json @@ -316,11 +316,6 @@ "internalType": "Claim", "name": "absolutePrestate", "type": "bytes32" - }, - { - "internalType": "bool", - "name": "disputeGameUsesSuperRoots", - "type": "bool" } ], "internalType": "struct OPContractsManager.OpChainConfig[]", diff --git a/packages/contracts-bedrock/snapshots/abi/OPContractsManagerUpgrader.json b/packages/contracts-bedrock/snapshots/abi/OPContractsManagerUpgrader.json index d9ac40df7ca..42887ce92c3 100644 --- a/packages/contracts-bedrock/snapshots/abi/OPContractsManagerUpgrader.json +++ b/packages/contracts-bedrock/snapshots/abi/OPContractsManagerUpgrader.json @@ -213,11 +213,6 @@ "internalType": "Claim", "name": "absolutePrestate", "type": "bytes32" - }, - { - "internalType": "bool", - "name": "disputeGameUsesSuperRoots", - "type": "bool" } ], "internalType": "struct OPContractsManager.OpChainConfig[]", diff --git a/packages/contracts-bedrock/snapshots/abi/OptimismPortal2.json b/packages/contracts-bedrock/snapshots/abi/OptimismPortal2.json index 5a60bf69cd6..9719bfe6504 100644 --- a/packages/contracts-bedrock/snapshots/abi/OptimismPortal2.json +++ b/packages/contracts-bedrock/snapshots/abi/OptimismPortal2.json @@ -285,11 +285,6 @@ "internalType": "contract IETHLockbox", "name": "_ethLockbox", "type": "address" - }, - { - "internalType": "bool", - "name": "_superRootsActive", - "type": "bool" } ], "name": "initialize", @@ -317,6 +312,24 @@ "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [ + { + "internalType": "contract IETHLockbox", + "name": "_newLockbox", + "type": "address" + }, + { + "internalType": "contract IAnchorStateRegistry", + "name": "_newAnchorStateRegistry", + "type": "address" + } + ], + "name": "migrateToSuperRoots", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, { "inputs": [ { @@ -738,19 +751,6 @@ "stateMutability": "view", "type": "function" }, - { - "inputs": [ - { - "internalType": "contract IETHLockbox", - "name": "_newLockbox", - "type": "address" - } - ], - "name": "updateLockbox", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, { "inputs": [ { @@ -762,11 +762,6 @@ "internalType": "contract IETHLockbox", "name": "_ethLockbox", "type": "address" - }, - { - "internalType": "bool", - "name": "_superRootsActive", - "type": "bool" } ], "name": "upgrade", @@ -824,18 +819,30 @@ "inputs": [ { "indexed": false, - "internalType": "address", + "internalType": "contract IETHLockbox", "name": "oldLockbox", "type": "address" }, { "indexed": false, - "internalType": "address", + "internalType": "contract IETHLockbox", "name": "newLockbox", "type": "address" + }, + { + "indexed": false, + "internalType": "contract IAnchorStateRegistry", + "name": "oldAnchorStateRegistry", + "type": "address" + }, + { + "indexed": false, + "internalType": "contract IAnchorStateRegistry", + "name": "newAnchorStateRegistry", + "type": "address" } ], - "name": "LockboxUpdated", + "name": "PortalMigrated", "type": "event" }, { @@ -1037,6 +1044,11 @@ "name": "OptimismPortal_InvalidSuperRootProof", "type": "error" }, + { + "inputs": [], + "name": "OptimismPortal_MigratingToSameRegistry", + "type": "error" + }, { "inputs": [], "name": "OptimismPortal_NoReentrancy", diff --git a/packages/contracts-bedrock/snapshots/semver-lock.json b/packages/contracts-bedrock/snapshots/semver-lock.json index b7bc2a93634..afb7d99f5f5 100644 --- a/packages/contracts-bedrock/snapshots/semver-lock.json +++ b/packages/contracts-bedrock/snapshots/semver-lock.json @@ -4,8 +4,8 @@ "sourceCodeHash": "0xe772f7db8033e4a738850cb28ac4849d3a454c93732135a8a10d4f7cb498088e" }, "src/L1/ETHLockbox.sol:ETHLockbox": { - "initCodeHash": "0x599593bf96170081d2fd191bb2f61eaa2d094faafb6a583a34b3eea2dc49e5c6", - "sourceCodeHash": "0x34da4a8a0e32cabce97056f10cc1c8231f14a1cd706ae63ee0bef3c1f65f3e13" + "initCodeHash": "0x3b5eac93a55c8fc20a4a517f5683aa1b2aa4424652b3bc2e9d4d6f4b6c9219e9", + "sourceCodeHash": "0x350aefb43cd1c40b7555473e09ad901970b813b7fd16ff4f009194810075685c" }, "src/L1/L1CrossDomainMessenger.sol:L1CrossDomainMessenger": { "initCodeHash": "0x5a6272f6bd4346da460b7ff2ebc426d158d7ffc65dc0f2c0651a9e6d545bfd03", @@ -20,12 +20,12 @@ "sourceCodeHash": "0x44797707aea8c63dec049a02d69ea056662a06e5cf320028ab8b388634bf1c67" }, "src/L1/OPContractsManager.sol:OPContractsManager": { - "initCodeHash": "0x6c18eff4e05c48f7ba034d3017d9aefb1332cd840482aa88b3322c9fec84d1a7", - "sourceCodeHash": "0x909957cabfa9af3303618c13de5efad528e8edeed9ce7b886c152b0ffac38d51" + "initCodeHash": "0x04ecca6917f73f77cf808097e48bf2fb025393907982438ae6fd9810f0f271b7", + "sourceCodeHash": "0x75ed03f4fbf1039e0f4df0c36de6944f341aa980d1e6d9087f8baceb3b19c851" }, "src/L1/OptimismPortal2.sol:OptimismPortal2": { - "initCodeHash": "0x774dd0c79fb11ea7ecbbffb28d1c005b72790577adbeeeb881c2a180e2feaadf", - "sourceCodeHash": "0x2d36fbd2326ceafe89ca1a3b80c223ad7f934e89e996d20b847498f015316bdf" + "initCodeHash": "0xbc7db7b1016025228d99a40db1760290b333bb90563dff5514fd253fd91019ba", + "sourceCodeHash": "0x367e0a1c609e3c82db41a8c5e056108cbbae58d61bfeb08707449655485ba8ab" }, "src/L1/ProtocolVersions.sol:ProtocolVersions": { "initCodeHash": "0x5a76c8530cb24cf23d3baacc6eefaac226382af13f1e2a35535d2ec2b0573b29", @@ -48,8 +48,8 @@ "sourceCodeHash": "0xb397f483d1d1e5bd15ab0184e9d994f662393c51e9f6d524bfc45e5f1d935601" }, "src/L2/ETHLiquidity.sol:ETHLiquidity": { - "initCodeHash": "0x776ece4a1bb24d97287806769470327641da240b083898a90943e2844957cc46", - "sourceCodeHash": "0xe5c08ce62327113e4bbaf29f47e5f1ddfad6fbd63c07132eedfba5af5325f331" + "initCodeHash": "0xfcb50f32bbbd7e426f83c5c320ea655b896c026324a5e51983c1b15041ef88ca", + "sourceCodeHash": "0x91e93fa27b6927d61039f5d8d22be9ec780583bcdba90a4c78c6e7945d5a9a81" }, "src/L2/GasPriceOracle.sol:GasPriceOracle": { "initCodeHash": "0x38ef70b2783dd45ad807afcf57972c7df4abaaeb5d16d17cdb451b9e931a9cbb", @@ -61,7 +61,7 @@ }, "src/L2/L1BlockInterop.sol:L1BlockInterop": { "initCodeHash": "0xa19bc53228445924ae016d499e42ad5bcc33f45e7a3b0c51e2f930ffb8dacd7a", - "sourceCodeHash": "0x42b3fee46a0c9b7b2c9369237b85dde4804fec4504fe619e67a7243840a8a86f" + "sourceCodeHash": "0xe959a150ea9ef814b13cdba6b9e9a4bf991e5c1325aa22c49795ce06cc35a8cd" }, "src/L2/L1FeeVault.sol:L1FeeVault": { "initCodeHash": "0x6745b7be3895a5e8d373df0066d931bae29c47672ac46c2f5829bd0052cc6d9e", @@ -88,8 +88,8 @@ "sourceCodeHash": "0xaef8ea36c5b78cd12e0e62811d51db627ccf0dfd2cc5479fb707a10ef0d42048" }, "src/L2/L2ToL2CrossDomainMessenger.sol:L2ToL2CrossDomainMessenger": { - "initCodeHash": "0xe0bd955e81e05b64ed8910c39785f65e020a859b78b99ed6d5536cdb30599e06", - "sourceCodeHash": "0x18cab4b0f38a6a0f9db8c37eede49d5bca5c03929ed1a1fc749a88df0f875322" + "initCodeHash": "0x2a6f9f717ca67d7920af1135a048e6cc3f1f655edc9aa72b6e00f23730319695", + "sourceCodeHash": "0x21590b7204311d006de0044a7c8f1c92e5468276e6db29e13c6103454c39c6d1" }, "src/L2/OperatorFeeVault.sol:OperatorFeeVault": { "initCodeHash": "0x3d8c0d7736e8767f2f797da1c20c5fe30bd7f48a4cf75f376290481ad7c0f91f", @@ -129,7 +129,7 @@ }, "src/L2/SuperchainWETH.sol:SuperchainWETH": { "initCodeHash": "0xc865e00858668be0eba7d0062a88aa0554b8f8fdae1db67cd8e1ab04cd014cde", - "sourceCodeHash": "0xeff949862649facbf683cf93a0b075fc52d08b28fca3f3fdef443f334d15202d" + "sourceCodeHash": "0x05f70cd9358ae4a17ace23f568c7952f8a81099278da6a46a601b8a101280a7e" }, "src/L2/WETH.sol:WETH": { "initCodeHash": "0x38b396fc35d72e8013bad2fe8d7dea5285499406d4c4b62e27c54252e1e0f00a", diff --git a/packages/contracts-bedrock/snapshots/storageLayout/ETHLockbox.json b/packages/contracts-bedrock/snapshots/storageLayout/ETHLockbox.json index 4ba870d921e..0f69cd3e54c 100644 --- a/packages/contracts-bedrock/snapshots/storageLayout/ETHLockbox.json +++ b/packages/contracts-bedrock/snapshots/storageLayout/ETHLockbox.json @@ -25,13 +25,13 @@ "label": "authorizedPortals", "offset": 0, "slot": "1", - "type": "mapping(address => bool)" + "type": "mapping(contract IOptimismPortal2 => bool)" }, { "bytes": "32", "label": "authorizedLockboxes", "offset": 0, "slot": "2", - "type": "mapping(address => bool)" + "type": "mapping(contract IETHLockbox => bool)" } ] \ No newline at end of file diff --git a/packages/contracts-bedrock/src/L1/ETHLockbox.sol b/packages/contracts-bedrock/src/L1/ETHLockbox.sol index 3931c90fb3e..364194a39db 100644 --- a/packages/contracts-bedrock/src/L1/ETHLockbox.sol +++ b/packages/contracts-bedrock/src/L1/ETHLockbox.sol @@ -34,46 +34,49 @@ contract ETHLockbox is ProxyAdminOwnedBase, Initializable, ISemver { /// @notice Thrown when the admin owner of the lockbox is different from the admin owner of the proxy admin. error ETHLockbox_DifferentProxyAdminOwner(); + /// @notice Thrown when any authorized portal has a different SuperchainConfig. + error ETHLockbox_DifferentSuperchainConfig(); + /// @notice Emitted when ETH is locked in the lockbox by an authorized portal. /// @param portal The address of the portal that locked the ETH. /// @param amount The amount of ETH locked. - event ETHLocked(address indexed portal, uint256 amount); + event ETHLocked(IOptimismPortal indexed portal, uint256 amount); /// @notice Emitted when ETH is unlocked from the lockbox by an authorized portal. /// @param portal The address of the portal that unlocked the ETH. /// @param amount The amount of ETH unlocked. - event ETHUnlocked(address indexed portal, uint256 amount); + event ETHUnlocked(IOptimismPortal indexed portal, uint256 amount); /// @notice Emitted when a portal is authorized to lock and unlock ETH. /// @param portal The address of the portal that was authorized. - event PortalAuthorized(address indexed portal); + event PortalAuthorized(IOptimismPortal indexed portal); /// @notice Emitted when an ETH lockbox is authorized to migrate its liquidity to the current ETH lockbox. /// @param lockbox The address of the ETH lockbox that was authorized. - event LockboxAuthorized(address indexed lockbox); + event LockboxAuthorized(IETHLockbox indexed lockbox); /// @notice Emitted when ETH liquidity is migrated from the current ETH lockbox to another. /// @param lockbox The address of the ETH lockbox that was migrated. - event LiquidityMigrated(address indexed lockbox, uint256 amount); + event LiquidityMigrated(IETHLockbox indexed lockbox, uint256 amount); /// @notice Emitted when ETH liquidity is received during an authorized lockbox migration. /// @param lockbox The address of the ETH lockbox that received the liquidity. /// @param amount The amount of ETH received. - event LiquidityReceived(address indexed lockbox, uint256 amount); + event LiquidityReceived(IETHLockbox indexed lockbox, uint256 amount); /// @notice The address of the SuperchainConfig contract. ISuperchainConfig public superchainConfig; /// @notice Mapping of authorized portals. - mapping(address => bool) public authorizedPortals; + mapping(IOptimismPortal => bool) public authorizedPortals; /// @notice Mapping of authorized lockboxes. - mapping(address => bool) public authorizedLockboxes; + mapping(IETHLockbox => bool) public authorizedLockboxes; /// @notice Semantic version. - /// @custom:semver 0.0.1 + /// @custom:semver 1.0.0 function version() public view virtual returns (string memory) { - return "0.0.1"; + return "1.0.0"; } /// @notice Constructs the ETHLockbox contract. @@ -93,33 +96,44 @@ contract ETHLockbox is ProxyAdminOwnedBase, Initializable, ISemver { { superchainConfig = ISuperchainConfig(_superchainConfig); for (uint256 i; i < _portals.length; i++) { - _authorizePortal(address(_portals[i])); + _authorizePortal(_portals[i]); } } + /// @notice Getter for the current paused status. + function paused() public view returns (bool) { + return superchainConfig.paused(); + } + /// @notice Authorizes a portal to lock and unlock ETH. /// @param _portal The address of the portal to authorize. function authorizePortal(IOptimismPortal _portal) external { + // Check that the sender is the proxy admin owner. if (msg.sender != proxyAdminOwner()) revert ETHLockbox_Unauthorized(); - _authorizePortal(address(_portal)); - } - /// @notice Getter for the current paused status. - function paused() public view returns (bool) { - return superchainConfig.paused(); + // Authorize the portal. + _authorizePortal(_portal); } /// @notice Receives the ETH liquidity migrated from an authorized lockbox. function receiveLiquidity() external payable { - if (!authorizedLockboxes[msg.sender]) revert ETHLockbox_Unauthorized(); - emit LiquidityReceived(msg.sender, msg.value); + // Check that the sender is authorized to trigger this function. + IETHLockbox sender = IETHLockbox(payable(msg.sender)); + if (!authorizedLockboxes[sender]) revert ETHLockbox_Unauthorized(); + + // Emit the event. + emit LiquidityReceived(sender, msg.value); } /// @notice Locks ETH in the lockbox. /// Called by an authorized portal on a deposit to lock the ETH value. function lockETH() external payable { - if (!authorizedPortals[msg.sender]) revert ETHLockbox_Unauthorized(); - emit ETHLocked(msg.sender, msg.value); + // Check that the sender is authorized to trigger this function. + IOptimismPortal sender = IOptimismPortal(payable(msg.sender)); + if (!authorizedPortals[sender]) revert ETHLockbox_Unauthorized(); + + // Emit the event. + emit ETHLocked(sender, msg.value); } /// @notice Unlocks ETH from the lockbox. @@ -127,27 +141,44 @@ contract ETHLockbox is ProxyAdminOwnedBase, Initializable, ISemver { /// Cannot be called if the lockbox is paused. /// @param _value The amount of ETH to unlock. function unlockETH(uint256 _value) external { + // Unlocks are blocked when paused, locks are not. if (paused()) revert ETHLockbox_Paused(); - if (!authorizedPortals[msg.sender]) revert ETHLockbox_Unauthorized(); + + // Check that the sender is authorized to trigger this function. + IOptimismPortal sender = IOptimismPortal(payable(msg.sender)); + if (!authorizedPortals[sender]) revert ETHLockbox_Unauthorized(); + + // Check that we have enough balance to process the unlock. if (_value > address(this).balance) revert ETHLockbox_InsufficientBalance(); - /// NOTE: Check l2Sender is not set to avoid this function to be called as a target on a withdrawal transaction - if (IOptimismPortal(payable(msg.sender)).l2Sender() != Constants.DEFAULT_L2_SENDER) { + + // Check that the sender is not executing a withdrawal transaction. + if (sender.l2Sender() != Constants.DEFAULT_L2_SENDER) { revert ETHLockbox_NoWithdrawalTransactions(); } - // Using `donateETH` to avoid triggering a deposit - IOptimismPortal(payable(msg.sender)).donateETH{ value: _value }(); - emit ETHUnlocked(msg.sender, _value); + // Using donateETH to avoid triggering a deposit. + sender.donateETH{ value: _value }(); + + // Emit the event. + emit ETHUnlocked(sender, _value); } - /// @notice Authorizes an ETH lockbox to migrate its liquidity to the current ETH lockbox. + /// @notice Authorizes an ETH lockbox to migrate its liquidity to the current ETH lockbox. We + /// allow this function to be called more than once for the same lockbox. A lockbox + /// cannot be removed from the authorized list once added. /// @param _lockbox The address of the ETH lockbox to authorize. function authorizeLockbox(IETHLockbox _lockbox) external { + // Check that the sender is the proxy admin owner. if (msg.sender != proxyAdminOwner()) revert ETHLockbox_Unauthorized(); + + // Check that the lockbox has the same proxy admin owner. if (!_sameProxyAdminOwner(address(_lockbox))) revert ETHLockbox_DifferentProxyAdminOwner(); - authorizedLockboxes[address(_lockbox)] = true; - emit LockboxAuthorized(address(_lockbox)); + // Authorize the lockbox. + authorizedLockboxes[_lockbox] = true; + + // Emit the event. + emit LockboxAuthorized(_lockbox); } /// @notice Migrates liquidity from the current ETH lockbox to another. @@ -156,18 +187,32 @@ contract ETHLockbox is ProxyAdminOwnedBase, Initializable, ISemver { /// from the ETHLockbox on finalized withdrawals. /// @param _lockbox The address of the ETH lockbox to migrate liquidity to. function migrateLiquidity(IETHLockbox _lockbox) external { + // Check that the sender is the proxy admin owner. if (msg.sender != proxyAdminOwner()) revert ETHLockbox_Unauthorized(); + + // Check that the lockbox has the same proxy admin owner. if (!_sameProxyAdminOwner(address(_lockbox))) revert ETHLockbox_DifferentProxyAdminOwner(); + // Receive the liquidity. IETHLockbox(_lockbox).receiveLiquidity{ value: address(this).balance }(); - emit LiquidityMigrated(address(_lockbox), address(this).balance); + + // Emit the event. + emit LiquidityMigrated(_lockbox, address(this).balance); } /// @notice Authorizes a portal to lock and unlock ETH. /// @param _portal The address of the portal to authorize. - function _authorizePortal(address _portal) internal { - if (!_sameProxyAdminOwner(_portal)) revert ETHLockbox_DifferentProxyAdminOwner(); + function _authorizePortal(IOptimismPortal _portal) internal { + // Check that the portal has the same proxy admin owner. + if (!_sameProxyAdminOwner(address(_portal))) revert ETHLockbox_DifferentProxyAdminOwner(); + + // Check that the portal has the same superchain config. + if (_portal.superchainConfig() != superchainConfig) revert ETHLockbox_DifferentSuperchainConfig(); + + // Authorize the portal. authorizedPortals[_portal] = true; + + // Emit the event. emit PortalAuthorized(_portal); } } diff --git a/packages/contracts-bedrock/src/L1/OPContractsManager.sol b/packages/contracts-bedrock/src/L1/OPContractsManager.sol index ae062ff00cb..556b07cc383 100644 --- a/packages/contracts-bedrock/src/L1/OPContractsManager.sol +++ b/packages/contracts-bedrock/src/L1/OPContractsManager.sol @@ -629,9 +629,7 @@ contract OPContractsManagerUpgrader is OPContractsManagerBase { } // Call `upgrade` on the OptimismPortal contract. - IOptimismPortal(payable(opChainAddrs.optimismPortal)).upgrade( - newAnchorStateRegistryProxy, ethLockbox, _opChainConfigs[i].disputeGameUsesSuperRoots - ); + IOptimismPortal(payable(opChainAddrs.optimismPortal)).upgrade(newAnchorStateRegistryProxy, ethLockbox); } // We also need to redeploy the dispute games because the AnchorStateRegistry is new. @@ -723,10 +721,17 @@ contract OPContractsManagerUpgrader is OPContractsManagerBase { // Modify the params with the new vm values. params.anchorStateRegistry = IAnchorStateRegistry(address(_newAnchorStateRegistryProxy)); params.vm = IBigStepper(impls.mipsImpl); - if (Claim.unwrap(_opChainConfig.absolutePrestate) == bytes32(0)) { + + // If the prestate is set in the config, use it. If not set, we'll try to use the prestate + // that already exists on the current dispute game. + if (Claim.unwrap(_opChainConfig.absolutePrestate) != bytes32(0)) { + params.absolutePrestate = _opChainConfig.absolutePrestate; + } + + // As a sanity check, if the prestate is zero here, revert. + if (params.absolutePrestate.raw() == bytes32(0)) { revert OPContractsManager.PrestateNotSet(); } - params.absolutePrestate = _opChainConfig.absolutePrestate; IDisputeGame newGame; if (GameType.unwrap(_gameType) == GameType.unwrap(GameTypes.PERMISSIONED_CANNON)) { @@ -888,7 +893,7 @@ contract OPContractsManagerDeployer is OPContractsManagerBase { output.opChainProxyAdmin, address(output.l1ERC721BridgeProxy), implementation.l1ERC721BridgeImpl, data ); - data = encodeOptimismPortalInitializer(_input, output, _superchainConfig); + data = encodeOptimismPortalInitializer(output, _superchainConfig); upgradeToAndCall( output.opChainProxyAdmin, address(output.optimismPortalProxy), implementation.optimismPortalImpl, data ); @@ -1043,7 +1048,6 @@ contract OPContractsManagerDeployer is OPContractsManagerBase { /// @notice Helper method for encoding the OptimismPortal initializer data. function encodeOptimismPortalInitializer( - OPContractsManager.DeployInput memory _input, OPContractsManager.DeployOutput memory _output, ISuperchainConfig _superchainConfig ) @@ -1054,13 +1058,7 @@ contract OPContractsManagerDeployer is OPContractsManagerBase { { return abi.encodeCall( IOptimismPortal.initialize, - ( - _output.systemConfigProxy, - _superchainConfig, - _output.anchorStateRegistryProxy, - _output.ethLockboxProxy, - _input.disputeGameUsesSuperRoots - ) + (_output.systemConfigProxy, _superchainConfig, _output.anchorStateRegistryProxy, _output.ethLockboxProxy) ); } @@ -1204,7 +1202,6 @@ contract OPContractsManager is ISemver { string saltMixer; uint64 gasLimit; // Configurable dispute game parameters. - bool disputeGameUsesSuperRoots; GameType disputeGameType; Claim disputeAbsolutePrestate; uint256 disputeMaxGameDepth; @@ -1272,7 +1269,6 @@ contract OPContractsManager is ISemver { ISystemConfig systemConfigProxy; IProxyAdmin proxyAdmin; Claim absolutePrestate; - bool disputeGameUsesSuperRoots; } struct AddGameInput { @@ -1298,9 +1294,9 @@ contract OPContractsManager is ISemver { // -------- Constants and Variables -------- - /// @custom:semver 1.10.1 + /// @custom:semver 1.11.1 function version() public pure virtual returns (string memory) { - return "1.10.1"; + return "1.11.1"; } OPContractsManagerGameTypeAdder public immutable opcmGameTypeAdder; diff --git a/packages/contracts-bedrock/src/L1/OptimismPortal2.sol b/packages/contracts-bedrock/src/L1/OptimismPortal2.sol index 5b8cef6cc83..a6478226fb0 100644 --- a/packages/contracts-bedrock/src/L1/OptimismPortal2.sol +++ b/packages/contracts-bedrock/src/L1/OptimismPortal2.sol @@ -160,7 +160,14 @@ contract OptimismPortal2 is Initializable, ResourceMetering, ReinitializableBase /// @notice Emitted when the ETHLockbox contract is updated. /// @param oldLockbox The address of the old ETHLockbox contract. /// @param newLockbox The address of the new ETHLockbox contract. - event LockboxUpdated(address oldLockbox, address newLockbox); + /// @param oldAnchorStateRegistry The address of the old AnchorStateRegistry contract. + /// @param newAnchorStateRegistry The address of the new AnchorStateRegistry contract. + event PortalMigrated( + IETHLockbox oldLockbox, + IETHLockbox newLockbox, + IAnchorStateRegistry oldAnchorStateRegistry, + IAnchorStateRegistry newAnchorStateRegistry + ); /// @notice Thrown when a withdrawal has already been finalized. error OptimismPortal_AlreadyFinalized(); @@ -222,6 +229,9 @@ contract OptimismPortal2 is Initializable, ResourceMetering, ReinitializableBase /// @notice Thrown when an output root chain id is invalid. error OptimismPortal_InvalidOutputRootChainId(); + /// @notice Thrown when trying to migrate to the same AnchorStateRegistry. + error OptimismPortal_MigratingToSameRegistry(); + /// @notice Reverts when paused. modifier whenNotPaused() { if (paused()) revert OptimismPortal_CallPaused(); @@ -229,9 +239,9 @@ contract OptimismPortal2 is Initializable, ResourceMetering, ReinitializableBase } /// @notice Semantic version. - /// @custom:semver 4.0.0 + /// @custom:semver 4.1.0 function version() public pure virtual returns (string memory) { - return "4.0.0"; + return "4.1.0"; } /// @param _proofMaturityDelaySeconds The proof maturity delay in seconds. @@ -245,13 +255,11 @@ contract OptimismPortal2 is Initializable, ResourceMetering, ReinitializableBase /// @param _superchainConfig Address of the SuperchainConfig. /// @param _anchorStateRegistry Address of the AnchorStateRegistry. /// @param _ethLockbox Contract of the ETHLockbox. - /// @param _superRootsActive Whether the OptimismPortal is using Super Roots or Output Roots. function initialize( ISystemConfig _systemConfig, ISuperchainConfig _superchainConfig, IAnchorStateRegistry _anchorStateRegistry, - IETHLockbox _ethLockbox, - bool _superRootsActive + IETHLockbox _ethLockbox ) external reinitializer(initVersion()) @@ -260,7 +268,6 @@ contract OptimismPortal2 is Initializable, ResourceMetering, ReinitializableBase superchainConfig = _superchainConfig; anchorStateRegistry = _anchorStateRegistry; ethLockbox = _ethLockbox; - superRootsActive = _superRootsActive; // Set the l2Sender slot, only if it is currently empty. This signals the first // initialization of the contract. @@ -274,18 +281,15 @@ contract OptimismPortal2 is Initializable, ResourceMetering, ReinitializableBase /// @notice Upgrades the OptimismPortal contract to have a reference to the AnchorStateRegistry. /// @param _anchorStateRegistry AnchorStateRegistry contract. /// @param _ethLockbox ETHLockbox contract. - /// @param _superRootsActive Whether the OptimismPortal is using Super Roots or Output Roots. function upgrade( IAnchorStateRegistry _anchorStateRegistry, - IETHLockbox _ethLockbox, - bool _superRootsActive + IETHLockbox _ethLockbox ) external reinitializer(initVersion()) { anchorStateRegistry = _anchorStateRegistry; ethLockbox = _ethLockbox; - superRootsActive = _superRootsActive; // Migrate the whole ETH balance to the ETHLockbox. _migrateLiquidity(); @@ -354,18 +358,46 @@ contract OptimismPortal2 is Initializable, ResourceMetering, ReinitializableBase // Intentionally empty. } - /// @notice Updates the ETHLockbox contract. - /// @dev This function MUST be called atomically with `ETHLockbox.migrateLiquidity()` - /// in the same transaction batch, or otherwise the OptimismPortal may not be able to - /// unlock ETH from the ETHLockbox on finalized withdrawals. + /// @notice Allows the owner of the ProxyAdmin to migrate the OptimismPortal to use a new + /// lockbox, point at a new AnchorStateRegistry, and start to use the Super Roots proof + /// method. Primarily used for OptimismPortal instances to join the interop set, but + /// can also be used to swap the proof method from Output Roots to Super Roots if the + /// provided lockbox is the same as the current one. + /// @dev It is possible to change lockboxes without migrating liquidity. This can cause one + /// of the OptimismPortal instances connected to the new lockbox to not be able to + /// unlock sufficient ETH to finalize withdrawals which would trigger reverts. To avoid + /// this issue, guarantee that this function is called atomically alongside the + /// ETHLockbox.migrateLiquidity() function within the same transaction. /// @param _newLockbox The address of the new ETHLockbox contract. - function updateLockbox(IETHLockbox _newLockbox) external { + function migrateToSuperRoots(IETHLockbox _newLockbox, IAnchorStateRegistry _newAnchorStateRegistry) external { + // Make sure the caller is the owner of the ProxyAdmin. if (msg.sender != proxyAdminOwner()) revert OptimismPortal_Unauthorized(); - address oldLockbox = address(ethLockbox); + // Chains can use this method to swap the proof method from Output Roots to Super Roots + // without joining the interop set. In this case, the old and new lockboxes will be the + // same. However, whether or not a chain is joining the interop set, all chains will need a + // new AnchorStateRegistry when migrating to Super Roots. We therefore check that the new + // AnchorStateRegistry is different than the old one to prevent this function from being + // accidentally misused. + if (anchorStateRegistry == _newAnchorStateRegistry) { + revert OptimismPortal_MigratingToSameRegistry(); + } + + // Update the ETHLockbox. + IETHLockbox oldLockbox = ethLockbox; ethLockbox = _newLockbox; - emit LockboxUpdated(oldLockbox, address(_newLockbox)); + // Update the AnchorStateRegistry. + IAnchorStateRegistry oldAnchorStateRegistry = anchorStateRegistry; + anchorStateRegistry = _newAnchorStateRegistry; + + // Set the proof method to Super Roots. We expect that migration will happen more than once + // for some chains (switching to single-chain Super Roots and then later joining the + // interop set) so we don't need to check that this is false. + superRootsActive = true; + + // Emit a PortalMigrated event. + emit PortalMigrated(oldLockbox, _newLockbox, oldAnchorStateRegistry, _newAnchorStateRegistry); } /// @notice Proves a withdrawal transaction using a Super Root proof. Only callable when the diff --git a/packages/contracts-bedrock/src/L2/ETHLiquidity.sol b/packages/contracts-bedrock/src/L2/ETHLiquidity.sol index 5a8c97f819b..6b9128e0a69 100644 --- a/packages/contracts-bedrock/src/L2/ETHLiquidity.sol +++ b/packages/contracts-bedrock/src/L2/ETHLiquidity.sol @@ -15,7 +15,8 @@ import { ISemver } from "interfaces/universal/ISemver.sol"; /// @custom:predeploy 0x4200000000000000000000000000000000000025 /// @title ETHLiquidity /// @notice The ETHLiquidity contract allows other contracts to access ETH liquidity without -/// needing to modify the EVM to generate new ETH. +/// needing to modify the EVM to generate new ETH. Contract comes "pre-loaded" with +/// uint248.max balance to prevent liquidity shortages. contract ETHLiquidity is ISemver { /// @notice Emitted when an address burns ETH liquidity. event LiquidityBurned(address indexed caller, uint256 value); @@ -24,8 +25,8 @@ contract ETHLiquidity is ISemver { event LiquidityMinted(address indexed caller, uint256 value); /// @notice Semantic version. - /// @custom:semver 1.0.0-beta.6 - string public constant version = "1.0.0-beta.6"; + /// @custom:semver 1.0.0-beta.7 + string public constant version = "1.0.0-beta.7"; /// @notice Allows an address to lock ETH liquidity into this contract. function burn() external payable { diff --git a/packages/contracts-bedrock/src/L2/L1BlockInterop.sol b/packages/contracts-bedrock/src/L2/L1BlockInterop.sol index 85316e55043..224a5f0a4c0 100644 --- a/packages/contracts-bedrock/src/L2/L1BlockInterop.sol +++ b/packages/contracts-bedrock/src/L2/L1BlockInterop.sol @@ -11,7 +11,14 @@ import { NotDepositor, NotCrossL2Inbox } from "src/libraries/L1BlockErrors.sol"; /// @custom:proxied true /// @custom:predeploy 0x4200000000000000000000000000000000000015 /// @title L1BlockInterop -/// @notice Interop extensions of L1Block. +/// @notice Manages deposit contexts within L2 blocks. A deposit context represents a series of +/// deposited transactions within a single block, starting with an L1 attributes transaction +/// and ending after the final deposit. +/// The expected sequence of operations in a deposit context is: +/// 1. L1 attributes transaction opens the deposit context (isDeposit = true) +/// 2. User deposits are executed (if any exist) +/// 3. L1 attributes transaction closes the deposit context (isDeposit = false) +/// Note: During upgrades, additional deposits may follow after this sequence. contract L1BlockInterop is L1Block { /// @notice Storage slot that the isDeposit is stored at. /// This is a custom slot that is not part of the standard storage layout. @@ -32,9 +39,9 @@ contract L1BlockInterop is L1Block { } } - /// @notice Updates the `isDeposit` flag and sets the L1 block values for an Interop upgraded chain. - /// It updates the L1 block values through the `setL1BlockValuesEcotone` function. - /// It forwards the calldata to the internally-used `setL1BlockValuesEcotone` function. + /// @notice Updates the isDeposit flag and sets the L1 block values for an Interop upgraded chain. + /// It updates the L1 block values through the setL1BlockValuesEcotone function. + /// It forwards the calldata to the internally-used setL1BlockValuesEcotone function. function setL1BlockValuesInterop() external { // Set the isDeposit flag to true. assembly { @@ -44,8 +51,8 @@ contract L1BlockInterop is L1Block { _setL1BlockValuesEcotone(); } - /// @notice Resets the isDeposit flag. - /// Should only be called by the depositor account after the deposits are complete. + /// @notice Resets the isDeposit flag, marking the end of a deposit context. + /// @dev Should only be called by the depositor account after the deposits are complete. function depositsComplete() external { if (msg.sender != DEPOSITOR_ACCOUNT()) revert NotDepositor(); diff --git a/packages/contracts-bedrock/src/L2/L2ToL2CrossDomainMessenger.sol b/packages/contracts-bedrock/src/L2/L2ToL2CrossDomainMessenger.sol index f7580ae103f..95c23bb3c95 100644 --- a/packages/contracts-bedrock/src/L2/L2ToL2CrossDomainMessenger.sol +++ b/packages/contracts-bedrock/src/L2/L2ToL2CrossDomainMessenger.sol @@ -35,9 +35,6 @@ error MessageAlreadyRelayed(); /// @notice Thrown when a reentrant call is detected. error ReentrantCall(); -/// @notice Thrown when a call to the target contract during message relay fails. -error TargetCallFailed(); - /// @custom:proxied true /// @custom:predeploy 0x4200000000000000000000000000000000000023 /// @title L2ToL2CrossDomainMessenger @@ -195,7 +192,9 @@ contract L2ToL2CrossDomainMessenger is ISemver, TransientReentrancyAware { (success, returnData_) = target.call{ value: msg.value }(message); if (!success) { - revert TargetCallFailed(); + assembly { + revert(add(32, returnData_), mload(returnData_)) + } } emit RelayedMessage(source, nonce, messageHash); @@ -221,6 +220,11 @@ contract L2ToL2CrossDomainMessenger is ISemver, TransientReentrancyAware { } /// @notice Decodes the payload of a SentMessage event. + /// @dev The payload format is as follows: + /// encodePacked( + /// encode(event selector, destination, target, nonce), + /// encode(sender, message) + /// ) /// @param _payload Payload of the SentMessage event. /// @return destination_ Destination chain ID. /// @return target_ Target contract of the message. diff --git a/packages/contracts-bedrock/src/L2/SuperchainWETH.sol b/packages/contracts-bedrock/src/L2/SuperchainWETH.sol index 71e58180f0c..69f92857ee9 100644 --- a/packages/contracts-bedrock/src/L2/SuperchainWETH.sol +++ b/packages/contracts-bedrock/src/L2/SuperchainWETH.sol @@ -20,7 +20,7 @@ import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; /// @custom:proxied true /// @custom:predeploy 0x4200000000000000000000000000000000000024 /// @title SuperchainWETH -/// @notice SuperchainWETH is a version of WETH that can be freely transfrered between chains +/// @notice SuperchainWETH is a version of WETH that can be freely transferred between chains /// within the superchain. SuperchainWETH can be converted into native ETH on chains that /// do not use a custom gas token. contract SuperchainWETH is WETH98, IERC7802, ISemver { diff --git a/packages/contracts-bedrock/test/L1/ETHLockbox.t.sol b/packages/contracts-bedrock/test/L1/ETHLockbox.t.sol index 17ade244554..962dba0fb75 100644 --- a/packages/contracts-bedrock/test/L1/ETHLockbox.t.sol +++ b/packages/contracts-bedrock/test/L1/ETHLockbox.t.sol @@ -21,12 +21,12 @@ import { ProxyAdmin } from "src/universal/ProxyAdmin.sol"; contract ETHLockboxTest is CommonTest { error InvalidInitialization(); - event ETHLocked(address indexed portal, uint256 amount); - event ETHUnlocked(address indexed portal, uint256 amount); - event PortalAuthorized(address indexed portal); - event LockboxAuthorized(address indexed lockbox); - event LiquidityMigrated(address indexed lockbox, uint256 amount); - event LiquidityReceived(address indexed lockbox, uint256 amount); + event ETHLocked(IOptimismPortal indexed portal, uint256 amount); + event ETHUnlocked(IOptimismPortal indexed portal, uint256 amount); + event PortalAuthorized(IOptimismPortal indexed portal); + event LockboxAuthorized(IETHLockbox indexed lockbox); + event LiquidityMigrated(IETHLockbox indexed lockbox, uint256 amount); + event LiquidityReceived(IETHLockbox indexed lockbox, uint256 amount); ProxyAdmin public proxyAdmin; address public proxyAdminOwner; @@ -45,7 +45,7 @@ contract ETHLockboxTest is CommonTest { /// @notice Tests the superchain config was correctly set during initialization. function test_initialization_succeeds() public view { assertEq(address(ethLockbox.superchainConfig()), address(superchainConfig)); - assertEq(ethLockbox.authorizedPortals(address(optimismPortal2)), true); + assertEq(ethLockbox.authorizedPortals(optimismPortal2), true); } /// @notice Tests it reverts when the contract is already initialized. @@ -72,6 +72,12 @@ contract ETHLockboxTest is CommonTest { assertEq(ethLockbox.paused(), true); } + /// @notice Tests that the version function returns a valid string. We avoid testing the + /// specific value of the string as it changes frequently. + function test_version_succeeds() public view { + assert(bytes(ethLockbox.version()).length > 0); + } + /// @notice Tests the liquidity is correctly received. function testFuzz_receiveLiquidity_succeeds(address _lockbox, uint256 _value) public { // Since on the fork the `_lockbox` fuzzed address doesn't exist, we skip the test @@ -88,7 +94,7 @@ contract ETHLockboxTest is CommonTest { ); // Authorize the lockbox if needed - if (!ethLockbox.authorizedLockboxes(_lockbox)) { + if (!ethLockbox.authorizedLockboxes(IETHLockbox(_lockbox))) { vm.prank(proxyAdminOwner); ethLockbox.authorizeLockbox(IETHLockbox(_lockbox)); } @@ -98,7 +104,7 @@ contract ETHLockboxTest is CommonTest { // Expect the `LiquidityReceived` event to be emitted vm.expectEmit(address(ethLockbox)); - emit LiquidityReceived(_lockbox, _value); + emit LiquidityReceived(IETHLockbox(_lockbox), _value); // Call the `receiveLiquidity` function vm.prank(address(_lockbox)); @@ -110,7 +116,7 @@ contract ETHLockboxTest is CommonTest { /// @notice Tests it reverts when the caller is not an authorized portal. function testFuzz_lockETH_unauthorizedPortal_reverts(address _caller) public { - vm.assume(!ethLockbox.authorizedPortals(_caller)); + vm.assume(!ethLockbox.authorizedPortals(IOptimismPortal2(payable(_caller)))); // Expect the revert with `Unauthorized` selector vm.expectRevert(IETHLockbox.ETHLockbox_Unauthorized.selector); @@ -134,7 +140,7 @@ contract ETHLockboxTest is CommonTest { // Look for the emit of the `ETHLocked` event vm.expectEmit(address(ethLockbox)); - emit ETHLocked(address(optimismPortal2), _amount); + emit ETHLocked(optimismPortal2, _amount); // Call the `lockETH` function with the portal vm.prank(address(optimismPortal2)); @@ -157,8 +163,14 @@ contract ETHLockboxTest is CommonTest { address(_portal), abi.encodeCall(IProxyAdminOwnedBase.proxyAdminOwner, ()), abi.encode(proxyAdminOwner) ); + // Mock the SuperchainConfig on the portal to be the same as the SuperchainConfig on the + // lockbox. + vm.mockCall( + address(_portal), abi.encodeCall(IOptimismPortal.superchainConfig, ()), abi.encode(superchainConfig) + ); + // Set the portal as an authorized portal if needed - if (!ethLockbox.authorizedPortals(address(_portal))) { + if (!ethLockbox.authorizedPortals(_portal)) { vm.prank(proxyAdminOwner); ethLockbox.authorizePortal(_portal); } @@ -171,7 +183,7 @@ contract ETHLockboxTest is CommonTest { // Look for the emit of the `ETHLocked` event vm.expectEmit(address(ethLockbox)); - emit ETHLocked(address(_portal), _amount); + emit ETHLocked(_portal, _amount); // Call the `lockETH` function with the portal vm.prank(address(_portal)); @@ -196,7 +208,7 @@ contract ETHLockboxTest is CommonTest { /// @notice Tests it reverts when the caller is not an authorized portal. function testFuzz_unlockETH_unauthorizedPortal_reverts(address _caller, uint256 _value) public { - vm.assume(!ethLockbox.authorizedPortals(_caller)); + vm.assume(!ethLockbox.authorizedPortals(IOptimismPortal2(payable(_caller)))); // Expect the revert with `Unauthorized` selector vm.expectRevert(IETHLockbox.ETHLockbox_Unauthorized.selector); @@ -249,7 +261,7 @@ contract ETHLockboxTest is CommonTest { // Look for the emit of the `ETHUnlocked` event vm.expectEmit(address(ethLockbox)); - emit ETHUnlocked(address(optimismPortal2), _value); + emit ETHUnlocked(optimismPortal2, _value); // Call the `unlockETH` function with the portal vm.prank(address(optimismPortal2)); @@ -269,8 +281,14 @@ contract ETHLockboxTest is CommonTest { address(_portal), abi.encodeCall(IProxyAdminOwnedBase.proxyAdminOwner, ()), abi.encode(proxyAdminOwner) ); + // Mock the SuperchainConfig on the portal to be the same as the SuperchainConfig on the + // lockbox. + vm.mockCall( + address(_portal), abi.encodeCall(IOptimismPortal.superchainConfig, ()), abi.encode(superchainConfig) + ); + // Set the portal as an authorized portal if needed - if (!ethLockbox.authorizedPortals(address(_portal))) { + if (!ethLockbox.authorizedPortals(_portal)) { vm.prank(proxyAdminOwner); ethLockbox.authorizePortal(_portal); } @@ -287,7 +305,7 @@ contract ETHLockboxTest is CommonTest { // Look for the emit of the `ETHUnlocked` event vm.expectEmit(address(ethLockbox)); - emit ETHUnlocked(address(optimismPortal2), _value); + emit ETHUnlocked(optimismPortal2, _value); // Call the `unlockETH` function with the portal vm.prank(address(optimismPortal2)); @@ -324,6 +342,28 @@ contract ETHLockboxTest is CommonTest { ethLockbox.authorizePortal(_portal); } + /// @notice Tests the authorizePortal function reverts when the portal has a different + /// SuperchainConfig than the one configured in the lockbox. + /// @param _portal The portal to authorize. + function testFuzz_authorizePortal_differentSuperchainConfig_reverts(IOptimismPortal2 _portal) public { + assumeNotForgeAddress(address(_portal)); + + // Mock the portal to have the right proxyAdminOwner. + vm.mockCall( + address(_portal), abi.encodeCall(IProxyAdminOwnedBase.proxyAdminOwner, ()), abi.encode(proxyAdminOwner) + ); + + // Mock the portal to have the wrong SuperchainConfig. + vm.mockCall(address(_portal), abi.encodeCall(IOptimismPortal.superchainConfig, ()), abi.encode(address(0))); + + // Expect the revert with `DifferentSuperchainConfig` selector + vm.expectRevert(IETHLockbox.ETHLockbox_DifferentSuperchainConfig.selector); + + // Call the `authorizePortal` function + vm.prank(proxyAdminOwner); + ethLockbox.authorizePortal(_portal); + } + /// @notice Tests the `authorizeLockbox` function succeeds using the `optimismPortal2` address as the portal. function test_authorizePortal_succeeds() public { // Calculate the correct storage slot for the mapping value @@ -336,14 +376,14 @@ contract ETHLockboxTest is CommonTest { // Expect the `PortalAuthorized` event to be emitted vm.expectEmit(address(ethLockbox)); - emit PortalAuthorized(address(optimismPortal2)); + emit PortalAuthorized(optimismPortal2); // Call the `authorizePortal` function with the portal vm.prank(proxyAdminOwner); ethLockbox.authorizePortal(optimismPortal2); // Assert the portal is authorized - assertTrue(ethLockbox.authorizedPortals(address(optimismPortal2))); + assertTrue(ethLockbox.authorizedPortals(optimismPortal2)); } /// @notice Tests the `authorizeLockbox` function succeeds @@ -355,16 +395,22 @@ contract ETHLockboxTest is CommonTest { address(_portal), abi.encodeCall(IProxyAdminOwnedBase.proxyAdminOwner, ()), abi.encode(proxyAdminOwner) ); + // Mock the SuperchainConfig on the portal to be the same as the SuperchainConfig on the + // Lockbox. + vm.mockCall( + address(_portal), abi.encodeCall(IOptimismPortal.superchainConfig, ()), abi.encode(superchainConfig) + ); + // Expect the `PortalAuthorized` event to be emitted vm.expectEmit(address(ethLockbox)); - emit PortalAuthorized(address(_portal)); + emit PortalAuthorized(_portal); // Call the `authorizePortal` function with the portal vm.prank(proxyAdminOwner); ethLockbox.authorizePortal(_portal); // Assert the portal is authorized - assertTrue(ethLockbox.authorizedPortals(address(_portal))); + assertTrue(ethLockbox.authorizedPortals(_portal)); } /// @notice Tests the `authorizeLockbox` function reverts when the caller is not the proxy admin. @@ -406,14 +452,14 @@ contract ETHLockboxTest is CommonTest { // Expect the `LockboxAuthorized` event to be emitted vm.expectEmit(address(ethLockbox)); - emit LockboxAuthorized(_lockbox); + emit LockboxAuthorized(IETHLockbox(_lockbox)); // Authorize the lockbox vm.prank(proxyAdminOwner); ethLockbox.authorizeLockbox(IETHLockbox(_lockbox)); // Assert the lockbox is authorized - assertTrue(ethLockbox.authorizedLockboxes(_lockbox)); + assertTrue(ethLockbox.authorizedLockboxes(IETHLockbox(_lockbox))); } /// @notice Tests the `migrateLiquidity` function reverts when the caller is not the proxy admin. @@ -457,9 +503,7 @@ contract ETHLockboxTest is CommonTest { vm.mockCall( address(_lockbox), abi.encodeCall(IProxyAdminOwnedBase.proxyAdminOwner, ()), abi.encode(proxyAdminOwner) ); - vm.mockCall( - address(_lockbox), abi.encodeCall(IETHLockbox.authorizedLockboxes, (address(ethLockbox))), abi.encode(true) - ); + vm.mockCall(address(_lockbox), abi.encodeCall(IETHLockbox.authorizedLockboxes, (ethLockbox)), abi.encode(true)); vm.mockCall(address(_lockbox), abi.encodeCall(IETHLockbox.receiveLiquidity, ()), abi.encode(true)); // Deal the balance to the lockbox @@ -471,7 +515,7 @@ contract ETHLockboxTest is CommonTest { // Expect the `LiquidityMigrated` event to be emitted vm.expectEmit(address(ethLockbox)); - emit LiquidityMigrated(_lockbox, ethLockboxBalanceBefore); + emit LiquidityMigrated(IETHLockbox(_lockbox), ethLockboxBalanceBefore); // Call the `migrateLiquidity` function with the lockbox vm.prank(proxyAdminOwner); diff --git a/packages/contracts-bedrock/test/L1/OPContractsManager.t.sol b/packages/contracts-bedrock/test/L1/OPContractsManager.t.sol index 80880840d8b..abdc2bb7c5c 100644 --- a/packages/contracts-bedrock/test/L1/OPContractsManager.t.sol +++ b/packages/contracts-bedrock/test/L1/OPContractsManager.t.sol @@ -32,11 +32,11 @@ import { IProxyAdmin } from "interfaces/universal/IProxyAdmin.sol"; import { ISuperchainConfig } from "interfaces/L1/ISuperchainConfig.sol"; import { IProtocolVersions } from "interfaces/L1/IProtocolVersions.sol"; import { IPreimageOracle } from "interfaces/cannon/IPreimageOracle.sol"; +import { IFaultDisputeGame } from "interfaces/dispute/IFaultDisputeGame.sol"; import { IPermissionedDisputeGame } from "interfaces/dispute/IPermissionedDisputeGame.sol"; import { IDelayedWETH } from "interfaces/dispute/IDelayedWETH.sol"; import { IDisputeGame } from "interfaces/dispute/IDisputeGame.sol"; import { IDisputeGameFactory } from "interfaces/dispute/IDisputeGameFactory.sol"; -import { IFaultDisputeGame } from "interfaces/dispute/IFaultDisputeGame.sol"; import { ISystemConfig } from "interfaces/L1/ISystemConfig.sol"; import { IOPContractsManager, @@ -46,7 +46,6 @@ import { IOPContractsManagerUpgrader, IOPContractsManagerContractsContainer } from "interfaces/L1/IOPContractsManager.sol"; -import { IOPContractsManagerLegacyUpgrade } from "interfaces/L1/IOPContractsManagerLegacyUpgrade.sol"; import { ISemver } from "interfaces/universal/ISemver.sol"; import { IETHLockbox } from "interfaces/L1/IETHLockbox.sol"; @@ -148,7 +147,6 @@ contract OPContractsManager_Deploy_Test is DeployOPChain_TestBase { startingAnchorRoot: _doi.startingAnchorRoot(), saltMixer: _doi.saltMixer(), gasLimit: _doi.gasLimit(), - disputeGameUsesSuperRoots: _doi.disputeGameUsesSuperRoots(), disputeGameType: _doi.disputeGameType(), disputeAbsolutePrestate: _doi.disputeAbsolutePrestate(), disputeMaxGameDepth: _doi.disputeMaxGameDepth(), @@ -273,8 +271,7 @@ contract OPContractsManager_Upgrade_Harness is CommonTest { IOPContractsManager.OpChainConfig({ systemConfigProxy: systemConfig, proxyAdmin: proxyAdmin, - absolutePrestate: absolutePrestate, - disputeGameUsesSuperRoots: false + absolutePrestate: absolutePrestate }) ); @@ -289,35 +286,12 @@ contract OPContractsManager_Upgrade_Harness is CommonTest { faultDisputeGame = IFaultDisputeGame(address(artifacts.mustGetAddress("FaultDisputeGame"))); } - /// @notice Converts the new OpChainConfig struct to the legacy OpChainConfig struct format. - /// This function is used to test Upgrade 13 and 14 paths and can be safely removed - /// after those upgrades are completed. Only difference in the new struct is the added - /// disputeGameUsesSuperRoots boolean. - /// @param _opChainConfigs The new OpChainConfig structs to convert. - /// @return The legacy OpChainConfig structs. - function convertToLegacyConfigs(IOPContractsManager.OpChainConfig[] memory _opChainConfigs) - public - pure - returns (IOPContractsManagerLegacyUpgrade.OpChainConfig[] memory) - { - IOPContractsManagerLegacyUpgrade.OpChainConfig[] memory legacyConfigs = - new IOPContractsManagerLegacyUpgrade.OpChainConfig[](_opChainConfigs.length); - for (uint256 i = 0; i < _opChainConfigs.length; i++) { - legacyConfigs[i] = IOPContractsManagerLegacyUpgrade.OpChainConfig({ - systemConfigProxy: _opChainConfigs[i].systemConfigProxy, - proxyAdmin: _opChainConfigs[i].proxyAdmin, - absolutePrestate: _opChainConfigs[i].absolutePrestate - }); - } - return legacyConfigs; - } - function expectEmitUpgraded(address impl, address proxy) public { vm.expectEmit(proxy); emit Upgraded(impl); } - function runV200UpgradeAndChecks(address _delegateCaller) public { + function runUpgrade13UpgradeAndChecks(address _delegateCaller) public { // The address below corresponds with the address of the v2.0.0-rc.1 OPCM on mainnet. IOPContractsManager deployedOPCM = IOPContractsManager(address(0x026b2F158255Beac46c1E7c6b8BbF29A4b6A7B76)); IOPCMImplementationsWithoutLockbox.Implementations memory impls = @@ -379,8 +353,7 @@ contract OPContractsManager_Upgrade_Harness is CommonTest { vm.etch(_delegateCaller, vm.getDeployedCode("test/mocks/Callers.sol:DelegateCaller")); DelegateCaller(_delegateCaller).dcForward( - address(deployedOPCM), - abi.encodeCall(IOPContractsManagerLegacyUpgrade.upgrade, (convertToLegacyConfigs(opChainConfigs))) + address(deployedOPCM), abi.encodeCall(IOPContractsManager.upgrade, (opChainConfigs)) ); VmSafe.Gas memory gas = vm.lastCallGas(); @@ -427,7 +400,6 @@ contract OPContractsManager_Upgrade_Harness is CommonTest { } function runUpgrade14UpgradeAndChecks(address _delegateCaller) public { - // TODO(#14665): Replace this address with once the final OPCM is deployed for Upgrade 14. IOPContractsManager deployedOPCM = IOPContractsManager(address(0x3A1f523a4bc09cd344A2745a108Bb0398288094F)); IOPCMImplementationsWithoutLockbox.Implementations memory impls = IOPCMImplementationsWithoutLockbox(address(deployedOPCM)).implementations(); @@ -461,8 +433,7 @@ contract OPContractsManager_Upgrade_Harness is CommonTest { vm.etch(_delegateCaller, vm.getDeployedCode("test/mocks/Callers.sol:DelegateCaller")); DelegateCaller(_delegateCaller).dcForward( - address(deployedOPCM), - abi.encodeCall(IOPContractsManagerLegacyUpgrade.upgrade, (convertToLegacyConfigs(opChainConfigs))) + address(deployedOPCM), abi.encodeCall(IOPContractsManager.upgrade, (opChainConfigs)) ); VmSafe.Gas memory gas = vm.lastCallGas(); @@ -590,7 +561,7 @@ contract OPContractsManager_Upgrade_Harness is CommonTest { // Make sure that the OptimismPortal is upgraded to the right version. It must also have a // reference to the new AnchorStateRegistry. - assertEq(ISemver(address(optimismPortal2)).version(), "4.0.0"); + assertEq(ISemver(address(optimismPortal2)).version(), "4.1.0"); assertEq(impls.optimismPortalImpl, EIP1967Helper.getImplementation(address(optimismPortal2))); assertEq(address(optimismPortal2.anchorStateRegistry()), address(newAsrProxy)); DeployUtils.assertInitialized({ @@ -609,7 +580,7 @@ contract OPContractsManager_Upgrade_Harness is CommonTest { function runUpgradeTestAndChecks(address _delegateCaller) public { // TODO(#14691): Remove this function once Upgrade 15 is deployed on Mainnet. - runV200UpgradeAndChecks(_delegateCaller); + runUpgrade13UpgradeAndChecks(_delegateCaller); // TODO(#14691): Remove this function once Upgrade 15 is deployed on Mainnet. runUpgrade14UpgradeAndChecks(_delegateCaller); runUpgrade15UpgradeAndChecks(_delegateCaller); @@ -677,13 +648,86 @@ contract OPContractsManager_Upgrade_Test is OPContractsManager_Upgrade_Harness { // Try to upgrade the current OPChain runUpgradeTestAndChecks(upgrader); } + + /// @notice Tests that the absolute prestate can be overridden using the upgrade config. + function test_upgrade_absolutePrestateOverride_succeeds() public { + // Run Upgrade 13 and 14 to get us to a state where we can run Upgrade 15. + // Can remove these two calls as Upgrade 13 and 14 are executed in prod. + runUpgrade13UpgradeAndChecks(upgrader); + runUpgrade14UpgradeAndChecks(upgrader); + + // Get the pdg and fdg before the upgrade + Claim pdgPrestateBefore = IPermissionedDisputeGame( + address(disputeGameFactory.gameImpls(GameTypes.PERMISSIONED_CANNON)) + ).absolutePrestate(); + Claim fdgPrestateBefore = + IFaultDisputeGame(address(disputeGameFactory.gameImpls(GameTypes.CANNON))).absolutePrestate(); + + // Assert that the prestate is not zero. + assertNotEq(pdgPrestateBefore.raw(), bytes32(0)); + assertNotEq(fdgPrestateBefore.raw(), bytes32(0)); + + // Set the absolute prestate input to something non-zero. + opChainConfigs[0].absolutePrestate = Claim.wrap(bytes32(uint256(1))); + + // Now run Upgrade 15. + runUpgrade15UpgradeAndChecks(upgrader); + + // Get the absolute prestate after the upgrade + Claim pdgPrestateAfter = IPermissionedDisputeGame( + address(disputeGameFactory.gameImpls(GameTypes.PERMISSIONED_CANNON)) + ).absolutePrestate(); + Claim fdgPrestateAfter = + IFaultDisputeGame(address(disputeGameFactory.gameImpls(GameTypes.CANNON))).absolutePrestate(); + + // Assert that the absolute prestate is the non-zero value we set. + assertEq(pdgPrestateAfter.raw(), bytes32(uint256(1))); + assertEq(fdgPrestateAfter.raw(), bytes32(uint256(1))); + } + + /// @notice Tests that the old absolute prestate is used if the upgrade config does not set an + /// absolute prestate. + function test_upgrade_absolutePrestateNotSet_succeeds() public { + // Run Upgrade 13 and 14 to get us to a state where we can run Upgrade 15. + // Can remove these two calls as Upgrade 13 and 14 are executed in prod. + runUpgrade13UpgradeAndChecks(upgrader); + runUpgrade14UpgradeAndChecks(upgrader); + + // Get the pdg and fdg before the upgrade + Claim pdgPrestateBefore = IPermissionedDisputeGame( + address(disputeGameFactory.gameImpls(GameTypes.PERMISSIONED_CANNON)) + ).absolutePrestate(); + Claim fdgPrestateBefore = + IFaultDisputeGame(address(disputeGameFactory.gameImpls(GameTypes.CANNON))).absolutePrestate(); + + // Assert that the prestate is not zero. + assertNotEq(pdgPrestateBefore.raw(), bytes32(0)); + assertNotEq(fdgPrestateBefore.raw(), bytes32(0)); + + // Set the absolute prestate input to zero. + opChainConfigs[0].absolutePrestate = Claim.wrap(bytes32(0)); + + // Now run Upgrade 15. + runUpgrade15UpgradeAndChecks(upgrader); + + // Get the absolute prestate after the upgrade + Claim pdgPrestateAfter = IPermissionedDisputeGame( + address(disputeGameFactory.gameImpls(GameTypes.PERMISSIONED_CANNON)) + ).absolutePrestate(); + Claim fdgPrestateAfter = + IFaultDisputeGame(address(disputeGameFactory.gameImpls(GameTypes.CANNON))).absolutePrestate(); + + // Assert that the absolute prestate is the same as before the upgrade. + assertEq(pdgPrestateAfter.raw(), pdgPrestateBefore.raw()); + assertEq(fdgPrestateAfter.raw(), fdgPrestateBefore.raw()); + } } contract OPContractsManager_Upgrade_TestFails is OPContractsManager_Upgrade_Harness { // Upgrade to U14 first function setUp() public override { super.setUp(); - runV200UpgradeAndChecks(upgrader); + runUpgrade13UpgradeAndChecks(upgrader); } function test_upgrade_notDelegateCalled_reverts() public { @@ -705,8 +749,24 @@ contract OPContractsManager_Upgrade_TestFails is OPContractsManager_Upgrade_Harn ); } + /// @notice Tests that upgrade reverts when absolutePrestate is zero and the existing game also + /// has an absolute prestate of zero. function test_upgrade_absolutePrestateNotSet_reverts() public { + // Set the config to try to update the absolutePrestate to zero. opChainConfigs[0].absolutePrestate = Claim.wrap(bytes32(0)); + + // Get the address of the PermissionedDisputeGame. + IPermissionedDisputeGame pdg = + IPermissionedDisputeGame(address(disputeGameFactory.gameImpls(GameTypes.PERMISSIONED_CANNON))); + + // Mock the PDG to return a prestate of zero. + vm.mockCall( + address(pdg), + abi.encodeCall(IPermissionedDisputeGame.absolutePrestate, ()), + abi.encode(Claim.wrap(bytes32(0))) + ); + + // Expect the upgrade to revert with PrestateNotSet. vm.expectRevert(IOPContractsManager.PrestateNotSet.selector); DelegateCaller(upgrader).dcForward(address(opcm), abi.encodeCall(IOPContractsManager.upgrade, (opChainConfigs))); } @@ -929,7 +989,6 @@ contract OPContractsManager_AddGameType_Test is Test { l2ChainId: 100, saltMixer: "hello", gasLimit: 30_000_000, - disputeGameUsesSuperRoots: false, disputeGameType: GameType.wrap(1), disputeAbsolutePrestate: Claim.wrap( bytes32(hex"038512e02c4c3f7bdaec27d00edf55b7155e0905301e1a88083e4e0a6764d54c") @@ -1286,7 +1345,6 @@ contract OPContractsManager_UpdatePrestate_Test is Test { l2ChainId: 100, saltMixer: "hello", gasLimit: 30_000_000, - disputeGameUsesSuperRoots: false, disputeGameType: GameType.wrap(1), disputeAbsolutePrestate: Claim.wrap( bytes32(hex"038512e02c4c3f7bdaec27d00edf55b7155e0905301e1a88083e4e0a6764d54c") @@ -1308,10 +1366,7 @@ contract OPContractsManager_UpdatePrestate_Test is Test { function test_updatePrestate_pdgOnlyWithValidInput_succeeds() public { IOPContractsManager.OpChainConfig[] memory inputs = new IOPContractsManager.OpChainConfig[](1); inputs[0] = IOPContractsManager.OpChainConfig( - chainDeployOutput.systemConfigProxy, - chainDeployOutput.opChainProxyAdmin, - Claim.wrap(bytes32(hex"ABBA")), - false + chainDeployOutput.systemConfigProxy, chainDeployOutput.opChainProxyAdmin, Claim.wrap(bytes32(hex"ABBA")) ); address proxyAdminOwner = chainDeployOutput.opChainProxyAdmin.owner(); @@ -1342,10 +1397,7 @@ contract OPContractsManager_UpdatePrestate_Test is Test { IOPContractsManager.OpChainConfig[] memory inputs = new IOPContractsManager.OpChainConfig[](1); inputs[0] = IOPContractsManager.OpChainConfig( - chainDeployOutput.systemConfigProxy, - chainDeployOutput.opChainProxyAdmin, - Claim.wrap(bytes32(hex"ABBA")), - false + chainDeployOutput.systemConfigProxy, chainDeployOutput.opChainProxyAdmin, Claim.wrap(bytes32(hex"ABBA")) ); address proxyAdminOwner = chainDeployOutput.opChainProxyAdmin.owner(); @@ -1382,8 +1434,7 @@ contract OPContractsManager_UpdatePrestate_Test is Test { inputs[0] = IOPContractsManager.OpChainConfig({ systemConfigProxy: chainDeployOutput.systemConfigProxy, proxyAdmin: chainDeployOutput.opChainProxyAdmin, - absolutePrestate: Claim.wrap(bytes32(0)), - disputeGameUsesSuperRoots: false + absolutePrestate: Claim.wrap(bytes32(0)) }); address proxyAdminOwner = chainDeployOutput.opChainProxyAdmin.owner(); diff --git a/packages/contracts-bedrock/test/L1/OptimismPortal2.t.sol b/packages/contracts-bedrock/test/L1/OptimismPortal2.t.sol index c0c05e6f327..35b8fb014c6 100644 --- a/packages/contracts-bedrock/test/L1/OptimismPortal2.t.sol +++ b/packages/contracts-bedrock/test/L1/OptimismPortal2.t.sol @@ -101,7 +101,7 @@ contract OptimismPortal2_Test is CommonTest { // Call the upgrade function. vm.prank(Predeploys.PROXY_ADMIN); - optimismPortal2.upgrade(IAnchorStateRegistry(_newAnchorStateRegistry), IETHLockbox(ethLockbox), true); + optimismPortal2.upgrade(IAnchorStateRegistry(_newAnchorStateRegistry), IETHLockbox(ethLockbox)); // Assert the portal is properly upgraded. assertEq(address(optimismPortal2.ethLockbox()), address(ethLockbox)); @@ -469,27 +469,52 @@ contract OptimismPortal2_Test is CommonTest { assertEq(accountAccesses[2].storageAccesses.length, 0); } - /// @dev Tests that `updateLockbox` reverts if the caller is not the proxy admin owner. - function testFuzz_updateLockbox_notProxyAdminOwner_reverts(address _caller) external { + /// @dev Tests that `migrateToSuperRoots` reverts if the caller is not the proxy admin owner. + function testFuzz_migrateToSuperRoots_notProxyAdminOwner_reverts(address _caller) external { vm.assume(_caller != optimismPortal2.proxyAdminOwner()); vm.expectRevert(IOptimismPortal.OptimismPortal_Unauthorized.selector); vm.prank(_caller); - optimismPortal2.updateLockbox(IETHLockbox(address(1))); + optimismPortal2.migrateToSuperRoots(IETHLockbox(address(1)), IAnchorStateRegistry(address(1))); } - /// @dev Tests that `updateLockbox` updates the ETHLockbox contract. - function testFuzz_updateLockbox_succeeds(address _newLockbox) external { + /// @dev Tests that `migrateToSuperRoots` reverts if the new registry is the same as the + /// current one. + /// @param _newLockbox The new ETHLockbox to migrate to. + function testFuzz_migrateToSuperRoots_usingSameRegistry_reverts(address _newLockbox) external { + vm.assume(_newLockbox != address(optimismPortal2.ethLockbox())); + + // Use the same registry as the current one. + IAnchorStateRegistry newAnchorStateRegistry = optimismPortal2.anchorStateRegistry(); + + // Trigger the call from the right address. + address caller = optimismPortal2.proxyAdminOwner(); + + // Expect the migration to revert. + vm.expectRevert(IOptimismPortal.OptimismPortal_MigratingToSameRegistry.selector); + vm.prank(caller); + optimismPortal2.migrateToSuperRoots(IETHLockbox(_newLockbox), newAnchorStateRegistry); + } + + /// @dev Tests that `migrateToSuperRoots` updates the ETHLockbox contract, updates the + /// AnchorStateRegistry, and sets the superRootsActive flag to true. + /// @param _newLockbox The new ETHLockbox to migrate to. + /// @param _newAnchorStateRegistry The new AnchorStateRegistry to migrate to. + function testFuzz_migrateToSuperRoots_succeeds(address _newLockbox, address _newAnchorStateRegistry) external { address oldLockbox = address(optimismPortal2.ethLockbox()); + address oldAnchorStateRegistry = address(optimismPortal2.anchorStateRegistry()); vm.assume(_newLockbox != oldLockbox); + vm.assume(_newAnchorStateRegistry != oldAnchorStateRegistry); vm.expectEmit(address(optimismPortal2)); - emit LockboxUpdated(oldLockbox, _newLockbox); + emit PortalMigrated(oldLockbox, _newLockbox, oldAnchorStateRegistry, _newAnchorStateRegistry); vm.prank(optimismPortal2.proxyAdminOwner()); - optimismPortal2.updateLockbox(IETHLockbox(_newLockbox)); + optimismPortal2.migrateToSuperRoots(IETHLockbox(_newLockbox), IAnchorStateRegistry(_newAnchorStateRegistry)); assertEq(address(optimismPortal2.ethLockbox()), _newLockbox); + assertEq(address(optimismPortal2.anchorStateRegistry()), _newAnchorStateRegistry); + assertTrue(optimismPortal2.superRootsActive()); } } @@ -2137,15 +2162,12 @@ contract OptimismPortal2_upgrade_Test is CommonTest { vm.store(address(optimismPortal2), bytes32(slot.slot), bytes32(0)); // Trigger upgrade(). - optimismPortal2.upgrade(IAnchorStateRegistry(address(0xdeadbeef)), IETHLockbox(ethLockbox), true); + optimismPortal2.upgrade(IAnchorStateRegistry(address(0xdeadbeef)), IETHLockbox(ethLockbox)); // Verify that the initialized slot was updated. bytes32 initializedSlotAfter = vm.load(address(optimismPortal2), bytes32(slot.slot)); assertEq(initializedSlotAfter, bytes32(uint256(2))); - // Verify that superRootsActive was set to true. - assertEq(optimismPortal2.superRootsActive(), true); - // Verify that the AnchorStateRegistry was set. assertEq(address(optimismPortal2.anchorStateRegistry()), address(0xdeadbeef)); } @@ -2159,11 +2181,11 @@ contract OptimismPortal2_upgrade_Test is CommonTest { vm.store(address(optimismPortal2), bytes32(slot.slot), bytes32(0)); // Trigger first upgrade. - optimismPortal2.upgrade(IAnchorStateRegistry(address(0xdeadbeef)), IETHLockbox(ethLockbox), true); + optimismPortal2.upgrade(IAnchorStateRegistry(address(0xdeadbeef)), IETHLockbox(ethLockbox)); // Try to trigger second upgrade. vm.expectRevert("Initializable: contract is already initialized"); - optimismPortal2.upgrade(IAnchorStateRegistry(address(0xdeadbeef)), IETHLockbox(ethLockbox), true); + optimismPortal2.upgrade(IAnchorStateRegistry(address(0xdeadbeef)), IETHLockbox(ethLockbox)); } /// @notice Tests that the upgrade() function reverts if called after initialization. @@ -2180,7 +2202,7 @@ contract OptimismPortal2_upgrade_Test is CommonTest { // Try to trigger upgrade(). vm.expectRevert("Initializable: contract is already initialized"); - optimismPortal2.upgrade(IAnchorStateRegistry(address(0xdeadbeef)), IETHLockbox(ethLockbox), true); + optimismPortal2.upgrade(IAnchorStateRegistry(address(0xdeadbeef)), IETHLockbox(ethLockbox)); } } diff --git a/packages/contracts-bedrock/test/L2/L2ToL2CrossDomainMessenger.t.sol b/packages/contracts-bedrock/test/L2/L2ToL2CrossDomainMessenger.t.sol index 0f12731289b..3bc99ae639f 100644 --- a/packages/contracts-bedrock/test/L2/L2ToL2CrossDomainMessenger.t.sol +++ b/packages/contracts-bedrock/test/L2/L2ToL2CrossDomainMessenger.t.sol @@ -19,8 +19,7 @@ import { MessageDestinationNotRelayChain, MessageTargetL2ToL2CrossDomainMessenger, MessageAlreadyRelayed, - ReentrantCall, - TargetCallFailed + ReentrantCall } from "src/L2/L2ToL2CrossDomainMessenger.sol"; // Interfaces @@ -463,8 +462,8 @@ contract L2ToL2CrossDomainMessengerTest is Test { returnData: "" }); - // Expect a revert with the TargetCallFailed selector - vm.expectRevert(TargetCallFailed.selector); + // Expect the target call to revert + vm.expectRevert(1); hoax(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER, _value); l2ToL2CrossDomainMessenger.relayMessage{ value: _value }(id, sentMessage); @@ -622,7 +621,8 @@ contract L2ToL2CrossDomainMessengerTest is Test { uint256 _value, uint256 _blockNum, uint256 _logIndex, - uint256 _time + uint256 _time, + bytes calldata _revertData ) external { @@ -633,7 +633,7 @@ contract L2ToL2CrossDomainMessengerTest is Test { if (_value > 0) assumePayable(_target); // Ensure that the target contract reverts - vm.mockCallRevert({ callee: _target, msgValue: _value, data: _message, revertData: abi.encode(false) }); + vm.mockCallRevert({ callee: _target, msgValue: _value, data: _message, revertData: _revertData }); Identifier memory id = Identifier(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER, _blockNum, _logIndex, _time, _source); @@ -649,8 +649,8 @@ contract L2ToL2CrossDomainMessengerTest is Test { returnData: "" }); - // Expect a revert with the TargetCallFailed selector - vm.expectRevert(TargetCallFailed.selector); + // Expect the target call to revert with the proper return data. + vm.expectRevert(_revertData); hoax(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER, _value); l2ToL2CrossDomainMessenger.relayMessage{ value: _value }(id, sentMessage); } diff --git a/packages/contracts-bedrock/test/opcm/UpgradeOPChain.t.sol b/packages/contracts-bedrock/test/opcm/UpgradeOPChain.t.sol index 8b4944be707..06e20c078c1 100644 --- a/packages/contracts-bedrock/test/opcm/UpgradeOPChain.t.sol +++ b/packages/contracts-bedrock/test/opcm/UpgradeOPChain.t.sol @@ -55,8 +55,7 @@ contract UpgradeOPChainInput_Test is Test { configs[0] = OPContractsManager.OpChainConfig({ systemConfigProxy: ISystemConfig(systemConfig1), proxyAdmin: IProxyAdmin(proxyAdmin1), - absolutePrestate: Claim.wrap(bytes32(uint256(1))), - disputeGameUsesSuperRoots: false + absolutePrestate: Claim.wrap(bytes32(uint256(1))) }); // Setup mock addresses and contracts for second config @@ -68,8 +67,7 @@ contract UpgradeOPChainInput_Test is Test { configs[1] = OPContractsManager.OpChainConfig({ systemConfigProxy: ISystemConfig(systemConfig2), proxyAdmin: IProxyAdmin(proxyAdmin2), - absolutePrestate: Claim.wrap(bytes32(uint256(2))), - disputeGameUsesSuperRoots: false + absolutePrestate: Claim.wrap(bytes32(uint256(2))) }); input.set(input.opChainConfigs.selector, configs); @@ -113,8 +111,7 @@ contract UpgradeOPChainInput_Test is Test { configs[0] = OPContractsManager.OpChainConfig({ systemConfigProxy: ISystemConfig(mockSystemConfig), proxyAdmin: IProxyAdmin(mockProxyAdmin), - absolutePrestate: Claim.wrap(bytes32(uint256(1))), - disputeGameUsesSuperRoots: false + absolutePrestate: Claim.wrap(bytes32(uint256(1))) }); vm.expectRevert("UpgradeOPCMInput: unknown selector"); @@ -150,8 +147,7 @@ contract UpgradeOPChain_Test is Test { config = OPContractsManager.OpChainConfig({ systemConfigProxy: ISystemConfig(makeAddr("systemConfigProxy")), proxyAdmin: IProxyAdmin(makeAddr("proxyAdmin")), - absolutePrestate: Claim.wrap(keccak256("absolutePrestate")), - disputeGameUsesSuperRoots: false + absolutePrestate: Claim.wrap(keccak256("absolutePrestate")) }); OPContractsManager.OpChainConfig[] memory configs = new OPContractsManager.OpChainConfig[](1); configs[0] = config; diff --git a/packages/contracts-bedrock/test/setup/Events.sol b/packages/contracts-bedrock/test/setup/Events.sol index 022258e02ba..64c63e65e6a 100644 --- a/packages/contracts-bedrock/test/setup/Events.sol +++ b/packages/contracts-bedrock/test/setup/Events.sol @@ -1,10 +1,12 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; -import { IDisputeGame } from "interfaces/dispute/IDisputeGame.sol"; +// Libraries +import { Types } from "src/libraries/Types.sol"; import "src/dispute/lib/Types.sol"; -import { Types } from "src/libraries/Types.sol"; +// Interfaces +import { IDisputeGame } from "interfaces/dispute/IDisputeGame.sol"; /// @title Events /// @dev Contains various events that are tested against. This contract needs to @@ -109,5 +111,7 @@ contract Events { event ETHMigrated(address indexed lockbox, uint256 ethBalance); - event LockboxUpdated(address oldLockbox, address newLockbox); + event PortalMigrated( + address oldLockbox, address newLockbox, address oldAnchorStateRegistry, address newAnchorStateRegistry + ); } diff --git a/packages/contracts-bedrock/test/setup/ForkLive.s.sol b/packages/contracts-bedrock/test/setup/ForkLive.s.sol index 6f3d663000e..70839e22bbf 100644 --- a/packages/contracts-bedrock/test/setup/ForkLive.s.sol +++ b/packages/contracts-bedrock/test/setup/ForkLive.s.sol @@ -26,7 +26,6 @@ import { IOPContractsManager } from "interfaces/L1/IOPContractsManager.sol"; import { IAnchorStateRegistry } from "interfaces/dispute/IAnchorStateRegistry.sol"; import { IETHLockbox } from "interfaces/L1/IETHLockbox.sol"; import { IOptimismPortal2 } from "interfaces/L1/IOptimismPortal2.sol"; -import { IOPContractsManagerLegacyUpgrade } from "interfaces/L1/IOPContractsManagerLegacyUpgrade.sol"; /// @title ForkLive /// @notice This script is called by Setup.sol as a preparation step for the foundry test suite, and is run as an @@ -190,8 +189,7 @@ contract ForkLive is Deployer { opChains[0] = IOPContractsManager.OpChainConfig({ systemConfigProxy: systemConfig, proxyAdmin: proxyAdmin, - absolutePrestate: Claim.wrap(bytes32(keccak256("absolutePrestate"))), - disputeGameUsesSuperRoots: false + absolutePrestate: Claim.wrap(bytes32(keccak256("absolutePrestate"))) }); // Temporarily replace the upgrader with a DelegateCaller so we can test the upgrade, @@ -199,27 +197,14 @@ contract ForkLive is Deployer { bytes memory upgraderCode = address(upgrader).code; vm.etch(upgrader, vm.getDeployedCode("test/mocks/Callers.sol:DelegateCaller")); - // Some upgrades require the legacy format. - IOPContractsManagerLegacyUpgrade.OpChainConfig[] memory legacyConfigs = - new IOPContractsManagerLegacyUpgrade.OpChainConfig[](opChains.length); - for (uint256 i = 0; i < opChains.length; i++) { - legacyConfigs[i] = IOPContractsManagerLegacyUpgrade.OpChainConfig({ - systemConfigProxy: opChains[i].systemConfigProxy, - proxyAdmin: opChains[i].proxyAdmin, - absolutePrestate: opChains[i].absolutePrestate - }); - } - // Start by doing Upgrade 13. DelegateCaller(upgrader).dcForward( - address(0x026b2F158255Beac46c1E7c6b8BbF29A4b6A7B76), - abi.encodeCall(IOPContractsManagerLegacyUpgrade.upgrade, (legacyConfigs)) + address(0x026b2F158255Beac46c1E7c6b8BbF29A4b6A7B76), abi.encodeCall(IOPContractsManager.upgrade, (opChains)) ); // Then do Upgrade 14. DelegateCaller(upgrader).dcForward( - address(0x3A1f523a4bc09cd344A2745a108Bb0398288094F), - abi.encodeCall(IOPContractsManagerLegacyUpgrade.upgrade, (legacyConfigs)) + address(0x3A1f523a4bc09cd344A2745a108Bb0398288094F), abi.encodeCall(IOPContractsManager.upgrade, (opChains)) ); // Then do the final upgrade. diff --git a/packages/contracts-bedrock/test/universal/Specs.t.sol b/packages/contracts-bedrock/test/universal/Specs.t.sol index 06752d20ef9..7bf4c2c8b41 100644 --- a/packages/contracts-bedrock/test/universal/Specs.t.sol +++ b/packages/contracts-bedrock/test/universal/Specs.t.sol @@ -210,7 +210,7 @@ contract Specification_Test is CommonTest { _addSpec({ _name: "OptimismPortal2", _sel: _getSel("checkWithdrawal(bytes32,address)") }); _addSpec({ _name: "OptimismPortal2", _sel: _getSel("depositTransaction(address,uint256,uint64,bool,bytes)") }); _addSpec({ _name: "OptimismPortal2", _sel: _getSel("donateETH()") }); - _addSpec({ _name: "OptimismPortal2", _sel: _getSel("updateLockbox(address)") }); + _addSpec({ _name: "OptimismPortal2", _sel: _getSel("migrateToSuperRoots(address,address)") }); _addSpec({ _name: "OptimismPortal2", _sel: IOptimismPortal.finalizeWithdrawalTransaction.selector, @@ -223,7 +223,7 @@ contract Specification_Test is CommonTest { }); _addSpec({ _name: "OptimismPortal2", _sel: _getSel("finalizedWithdrawals(bytes32)") }); _addSpec({ _name: "OptimismPortal2", _sel: _getSel("guardian()") }); - _addSpec({ _name: "OptimismPortal2", _sel: _getSel("initialize(address,address,address,address,bool)") }); + _addSpec({ _name: "OptimismPortal2", _sel: _getSel("initialize(address,address,address,address)") }); _addSpec({ _name: "OptimismPortal2", _sel: _getSel("l2Sender()") }); _addSpec({ _name: "OptimismPortal2", _sel: _getSel("minimumGasLimit(uint64)") }); _addSpec({ _name: "OptimismPortal2", _sel: _getSel("params()") }); @@ -255,7 +255,7 @@ contract Specification_Test is CommonTest { _addSpec({ _name: "OptimismPortal2", _sel: _getSel("respectedGameTypeUpdatedAt()") }); _addSpec({ _name: "OptimismPortal2", _sel: _getSel("proofSubmitters(bytes32,uint256)") }); _addSpec({ _name: "OptimismPortal2", _sel: _getSel("numProofSubmitters(bytes32)") }); - _addSpec({ _name: "OptimismPortal2", _sel: _getSel("upgrade(address,address,bool)") }); + _addSpec({ _name: "OptimismPortal2", _sel: _getSel("upgrade(address,address)") }); _addSpec({ _name: "OptimismPortal2", _sel: _getSel("ethLockbox()") }); _addSpec({ _name: "OptimismPortal2", _sel: _getSel("migrateLiquidity()") }); _addSpec({ _name: "OptimismPortal2", _sel: _getSel("proxyAdminOwner()") }); diff --git a/packages/contracts-bedrock/test/vendor/Initializable.t.sol b/packages/contracts-bedrock/test/vendor/Initializable.t.sol index 1afaf02d6c8..0d3ec2b5e79 100644 --- a/packages/contracts-bedrock/test/vendor/Initializable.t.sol +++ b/packages/contracts-bedrock/test/vendor/Initializable.t.sol @@ -124,7 +124,7 @@ contract Initializer_Test is CommonTest { name: "OptimismPortal2Impl", target: EIP1967Helper.getImplementation(address(optimismPortal2)), initCalldata: abi.encodeCall( - optimismPortal2.initialize, (systemConfig, superchainConfig, anchorStateRegistry, ethLockbox, false) + optimismPortal2.initialize, (systemConfig, superchainConfig, anchorStateRegistry, ethLockbox) ) }) ); @@ -134,7 +134,7 @@ contract Initializer_Test is CommonTest { name: "OptimismPortal2Proxy", target: address(optimismPortal2), initCalldata: abi.encodeCall( - optimismPortal2.initialize, (systemConfig, superchainConfig, anchorStateRegistry, ethLockbox, false) + optimismPortal2.initialize, (systemConfig, superchainConfig, anchorStateRegistry, ethLockbox) ) }) );