Commit ce2ce43b authored by smartcontracts's avatar smartcontracts Committed by GitHub

feat: have AnchorStateRegistry use a single root (#13700)

* feat: have AnchorStateRegistry use a single root

Updates the AnchorStateRegistry to use a single unified anchor
root by checking with the OptimismPortal for the currently
respected game type. Additionally makes the AnchorStateRegistry
MCP ready.

Users MUST deploy this contract as a new proxy and cannot upgrade
their existing proxy.

* Update snapshots post-merge

* op-deployer: Support single ASR

* Update semver

* updates after rebase

* rename method

* make isGameProper comment more obvious

* update semver

* semver bump

* simpler api for boolean functions

* comment updates

* address wildmolasses comments

* add test for not guardian

* a few more tests

---------
Co-authored-by: default avatarMatthew Slipper <me@matthewslipper.com>
parent 544c42b3
...@@ -196,7 +196,7 @@ func DeployL2ToL1(l1Host *script.Host, superCfg *SuperchainConfig, superDeployme ...@@ -196,7 +196,7 @@ func DeployL2ToL1(l1Host *script.Host, superCfg *SuperchainConfig, superDeployme
l1Host.SetTxOrigin(cfg.Deployer) l1Host.SetTxOrigin(cfg.Deployer)
output, err := opcm.DeployOPChainV160(l1Host, opcm.DeployOPChainInputV160{ output, err := opcm.DeployOPChain(l1Host, opcm.DeployOPChainInput{
OpChainProxyAdminOwner: cfg.ProxyAdminOwner, OpChainProxyAdminOwner: cfg.ProxyAdminOwner,
SystemConfigOwner: cfg.SystemConfigOwner, SystemConfigOwner: cfg.SystemConfigOwner,
Batcher: cfg.BatchSenderAddress, Batcher: cfg.BatchSenderAddress,
...@@ -215,7 +215,6 @@ func DeployL2ToL1(l1Host *script.Host, superCfg *SuperchainConfig, superDeployme ...@@ -215,7 +215,6 @@ func DeployL2ToL1(l1Host *script.Host, superCfg *SuperchainConfig, superDeployme
DisputeSplitDepth: cfg.DisputeSplitDepth, DisputeSplitDepth: cfg.DisputeSplitDepth,
DisputeClockExtension: cfg.DisputeClockExtension, DisputeClockExtension: cfg.DisputeClockExtension,
DisputeMaxClockDuration: cfg.DisputeMaxClockDuration, DisputeMaxClockDuration: cfg.DisputeMaxClockDuration,
StartingAnchorRoots: opcm.PermissionedGameStartingAnchorRoots,
}) })
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to deploy L2 OP chain: %w", err) return nil, fmt.Errorf("failed to deploy L2 OP chain: %w", err)
......
...@@ -20,6 +20,7 @@ type Implementations struct { ...@@ -20,6 +20,7 @@ type Implementations struct {
L1StandardBridgeImpl common.Address `json:"L1StandardBridgeImpl"` L1StandardBridgeImpl common.Address `json:"L1StandardBridgeImpl"`
OptimismMintableERC20FactoryImpl common.Address `json:"OptimismMintableERC20FactoryImpl"` OptimismMintableERC20FactoryImpl common.Address `json:"OptimismMintableERC20FactoryImpl"`
DisputeGameFactoryImpl common.Address `json:"DisputeGameFactoryImpl"` DisputeGameFactoryImpl common.Address `json:"DisputeGameFactoryImpl"`
AnchorStateRegistryImpl common.Address `json:"AnchorStateRegistryImpl"`
} }
type SuperchainDeployment struct { type SuperchainDeployment struct {
......
package integration_test package integration_test
import ( import (
"bufio"
"bytes" "bytes"
"compress/gzip"
"context" "context"
"crypto/rand"
"encoding/hex" "encoding/hex"
"encoding/json"
"fmt" "fmt"
"log/slog" "log/slog"
"maps"
"math/big" "math/big"
"os"
"strings" "strings"
"testing" "testing"
"time"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/rpc"
"github.com/lmittmann/w3"
"github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/broadcaster" "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/broadcaster"
"github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/opcm" "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/opcm"
"github.com/ethereum-optimism/optimism/op-deployer/pkg/env" "github.com/ethereum-optimism/optimism/op-deployer/pkg/env"
"github.com/ethereum-optimism/optimism/op-e2e/e2eutils/retryproxy"
altda "github.com/ethereum-optimism/optimism/op-alt-da" altda "github.com/ethereum-optimism/optimism/op-alt-da"
"github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/inspect" "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/inspect"
"github.com/ethereum-optimism/optimism/op-node/rollup" "github.com/ethereum-optimism/optimism/op-node/rollup"
...@@ -38,7 +25,6 @@ import ( ...@@ -38,7 +25,6 @@ import (
"github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/standard" "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/standard"
"github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/state" "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/state"
"github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/testutil" "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/testutil"
"github.com/ethereum-optimism/optimism/op-service/testutils/anvil"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
op_e2e "github.com/ethereum-optimism/optimism/op-e2e" op_e2e "github.com/ethereum-optimism/optimism/op-e2e"
...@@ -82,8 +68,6 @@ network_params: ...@@ -82,8 +68,6 @@ network_params:
const defaultL1ChainID uint64 = 77799777 const defaultL1ChainID uint64 = 77799777
var versionFunc = w3.MustNewFunc("version()", "string")
type deployerKey struct{} type deployerKey struct{}
func (d *deployerKey) HDPath() string { func (d *deployerKey) HDPath() string {
...@@ -202,406 +186,6 @@ func TestEndToEndApply(t *testing.T) { ...@@ -202,406 +186,6 @@ func TestEndToEndApply(t *testing.T) {
}) })
} }
type existingOPCMTest struct {
name string
network string
l1Release string
l2Release string
l2AllocsFile string
l1Semvers *expectedL1Semvers
l2Semvers *inspect.L2PredeploySemvers
}
type expectedL1Semvers struct {
SystemConfig string
PermissionedDisputeGame string
MIPS string
OptimismPortal string
AnchorStateRegistry string
DelayedWETH string
DisputeGameFactory string
PreimageOracle string
L1CrossDomainMessenger string
L1ERC721Bridge string
L1StandardBridge string
OptimismMintableERC20 string
}
func TestApplyExistingOPCM(t *testing.T) {
expectedL2SemversV160 := &inspect.L2PredeploySemvers{
L2ToL1MessagePasser: "1.1.1-beta.1",
DeployerWhitelist: "1.1.1-beta.1",
WETH: "1.0.0-beta.1",
L2CrossDomainMessenger: "2.1.1-beta.1",
L2StandardBridge: "1.11.1-beta.1",
SequencerFeeVault: "1.5.0-beta.2",
OptimismMintableERC20Factory: "1.10.1-beta.2",
L1BlockNumber: "1.1.1-beta.1",
GasPriceOracle: "1.3.1-beta.1",
L1Block: "1.5.1-beta.1",
LegacyMessagePasser: "1.1.1-beta.1",
L2ERC721Bridge: "1.7.1-beta.2",
OptimismMintableERC721Factory: "1.4.1-beta.1",
BaseFeeVault: "1.5.0-beta.2",
L1FeeVault: "1.5.0-beta.2",
SchemaRegistry: "1.3.1-beta.1",
EAS: "1.4.1-beta.1",
CrossL2Inbox: "",
L2toL2CrossDomainMessenger: "",
SuperchainWETH: "",
ETHLiquidity: "",
SuperchainTokenBridge: "",
OptimismMintableERC20: "1.4.0-beta.1",
OptimismMintableERC721: "1.3.1-beta.1",
}
expectedL1SemversV160 := &expectedL1Semvers{
SystemConfig: "2.2.0",
PermissionedDisputeGame: "1.3.1-beta.3", // Deployment bug
MIPS: "1.1.0",
OptimismPortal: "3.10.0",
AnchorStateRegistry: "2.0.1-beta.3", // Deployment bug
DelayedWETH: "1.1.0",
DisputeGameFactory: "1.0.0",
PreimageOracle: "1.1.2",
L1CrossDomainMessenger: "2.3.0",
L1ERC721Bridge: "2.1.0",
L1StandardBridge: "2.1.0",
OptimismMintableERC20: "1.9.0",
}
expectedL1SemversV180 := &expectedL1Semvers{
SystemConfig: "2.3.0",
PermissionedDisputeGame: "1.3.1",
MIPS: "1.2.1",
OptimismPortal: "3.10.0",
AnchorStateRegistry: "2.0.1-beta.3", // Deployment bug persisting across releases
DelayedWETH: "1.1.0",
DisputeGameFactory: "1.0.0",
PreimageOracle: "1.1.2",
L1CrossDomainMessenger: "2.3.0",
L1ERC721Bridge: "2.1.0",
L1StandardBridge: "2.1.0",
OptimismMintableERC20: "1.9.0",
}
tests := []existingOPCMTest{
{
"mainnet v1.6.0",
"mainnet",
"op-contracts/v1.6.0",
"op-contracts/v1.7.0-beta.1+l2-contracts",
"allocs-l2-v160-1.json.gz",
expectedL1SemversV160,
expectedL2SemversV160,
},
{
"sepolia v1.6.0",
"sepolia",
"op-contracts/v1.6.0",
"op-contracts/v1.7.0-beta.1+l2-contracts",
"allocs-l2-v160-11155111.json.gz",
expectedL1SemversV160,
expectedL2SemversV160,
},
{
"sepolia v1.8.0-rc.4",
"sepolia",
"op-contracts/v1.8.0-rc.4",
// The L2 predeploys need to still be the v1.7.0 beta contracts.
"op-contracts/v1.7.0-beta.1+l2-contracts",
// The L2 predeploys do not change in version 1.8.0.
"allocs-l2-v160-11155111.json.gz",
expectedL1SemversV180,
expectedL2SemversV160,
},
{
"mainnet v1.8.0-rc.4",
"mainnet",
"op-contracts/v1.8.0-rc.4",
// The L2 predeploys need to still be the v1.7.0 beta contracts.
"op-contracts/v1.7.0-beta.1+l2-contracts",
// The L2 predeploys do not change in version 1.8.0.
"allocs-l2-v160-1.json.gz",
expectedL1SemversV180,
expectedL2SemversV160,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
testApplyExistingOPCM(t, tt)
})
}
}
func testApplyExistingOPCM(t *testing.T, testInfo existingOPCMTest) {
op_e2e.InitParallel(t)
var forkRPCUrl string
var l1Versions standard.L1Versions
var l1ChainID uint64
if testInfo.network == "mainnet" {
forkRPCUrl = os.Getenv("MAINNET_RPC_URL")
l1Versions = standard.L1VersionsMainnet
l1ChainID = 1
} else if testInfo.network == "sepolia" {
forkRPCUrl = os.Getenv("SEPOLIA_RPC_URL")
l1Versions = standard.L1VersionsSepolia
l1ChainID = 11155111
} else {
t.Fatalf("invalid network: %s", testInfo.network)
}
require.NotEmpty(t, forkRPCUrl, "no fork RPC URL provided")
lgr := testlog.Logger(t, slog.LevelDebug)
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
defer cancel()
retryProxy := retryproxy.New(lgr, forkRPCUrl)
require.NoError(t, retryProxy.Start())
t.Cleanup(func() {
require.NoError(t, retryProxy.Stop())
})
runner, err := anvil.New(
retryProxy.Endpoint(),
lgr,
)
require.NoError(t, err)
require.NoError(t, runner.Start(ctx))
t.Cleanup(func() {
require.NoError(t, runner.Stop())
})
l1RPC, err := rpc.Dial(runner.RPCUrl())
require.NoError(t, err)
l1Client := ethclient.NewClient(l1RPC)
require.NoError(t, err)
l1ChainIDBig := new(big.Int).SetUint64(l1ChainID)
dk, err := devkeys.NewMnemonicDevKeys(devkeys.TestMnemonic)
require.NoError(t, err)
// index 0 from Anvil's test set
pk, err := crypto.HexToECDSA("ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80")
require.NoError(t, err)
l2ChainID := uint256.NewInt(777)
// Hardcode the below tags to ensure the test is validating the correct
// version even if the underlying tag changes
intent, st := newIntent(
t,
l1ChainIDBig,
dk,
l2ChainID,
artifacts.MustNewLocatorFromTag(testInfo.l1Release),
artifacts.MustNewLocatorFromTag(testInfo.l2Release),
)
// NOTE: the reference allocs for version 1.6 contain the gov token, so we need to enable it
// via override here.
intent.GlobalDeployOverrides = map[string]any{
"enableGovernance": true,
}
// Define a new create2 salt to avoid contract address collisions
_, err = rand.Read(st.Create2Salt[:])
require.NoError(t, err)
require.NoError(t, deployer.ApplyPipeline(
ctx,
deployer.ApplyPipelineOpts{
DeploymentTarget: deployer.DeploymentTargetLive,
L1RPCUrl: runner.RPCUrl(),
DeployerPrivateKey: pk,
Intent: intent,
State: st,
Logger: lgr,
StateWriter: pipeline.NoopStateWriter(),
},
))
validateOPChainDeployment(t, ethClientCodeGetter(ctx, l1Client), st, intent, true)
releases := l1Versions[testInfo.l1Release]
implTests := []struct {
name string
expAddr common.Address
actAddr common.Address
}{
{"OptimismPortal", releases.OptimismPortal.ImplementationAddress, st.ImplementationsDeployment.OptimismPortalImplAddress},
{"SystemConfig,", releases.SystemConfig.ImplementationAddress, st.ImplementationsDeployment.SystemConfigImplAddress},
{"L1CrossDomainMessenger", releases.L1CrossDomainMessenger.ImplementationAddress, st.ImplementationsDeployment.L1CrossDomainMessengerImplAddress},
{"L1ERC721Bridge", releases.L1ERC721Bridge.ImplementationAddress, st.ImplementationsDeployment.L1ERC721BridgeImplAddress},
{"L1StandardBridge", releases.L1StandardBridge.ImplementationAddress, st.ImplementationsDeployment.L1StandardBridgeImplAddress},
{"OptimismMintableERC20Factory", releases.OptimismMintableERC20Factory.ImplementationAddress, st.ImplementationsDeployment.OptimismMintableERC20FactoryImplAddress},
{"DisputeGameFactory", releases.DisputeGameFactory.ImplementationAddress, st.ImplementationsDeployment.DisputeGameFactoryImplAddress},
{"MIPS", releases.MIPS.Address, st.ImplementationsDeployment.MipsSingletonAddress},
{"PreimageOracle", releases.PreimageOracle.Address, st.ImplementationsDeployment.PreimageOracleSingletonAddress},
{"DelayedWETH", releases.DelayedWETH.ImplementationAddress, st.ImplementationsDeployment.DelayedWETHImplAddress},
}
for _, tt := range implTests {
require.Equal(t, tt.expAddr, tt.actAddr, "unexpected address for %s", tt.name)
}
chainState := st.Chains[0]
versionTests := []struct {
name string
expVersion string
addr common.Address
}{
{"SystemConfig", testInfo.l1Semvers.SystemConfig, chainState.SystemConfigProxyAddress},
{"PermissionedDisputeGame", testInfo.l1Semvers.PermissionedDisputeGame, chainState.PermissionedDisputeGameAddress},
{"MIPS", testInfo.l1Semvers.MIPS, st.ImplementationsDeployment.MipsSingletonAddress},
{"OptimismPortal", testInfo.l1Semvers.OptimismPortal, chainState.OptimismPortalProxyAddress},
{"AnchorStateRegistry", testInfo.l1Semvers.AnchorStateRegistry, chainState.AnchorStateRegistryProxyAddress},
{"DelayedWETH", testInfo.l1Semvers.DelayedWETH, chainState.DelayedWETHPermissionedGameProxyAddress},
{"DisputeGameFactory", testInfo.l1Semvers.DisputeGameFactory, chainState.DisputeGameFactoryProxyAddress},
{"PreimageOracle", testInfo.l1Semvers.PreimageOracle, st.ImplementationsDeployment.PreimageOracleSingletonAddress},
{"L1CrossDomainMessenger", testInfo.l1Semvers.L1CrossDomainMessenger, chainState.L1CrossDomainMessengerProxyAddress},
{"L1ERC721Bridge", testInfo.l1Semvers.L1ERC721Bridge, chainState.L1ERC721BridgeProxyAddress},
{"L1StandardBridge", testInfo.l1Semvers.L1StandardBridge, chainState.L1StandardBridgeProxyAddress},
{"OptimismMintableERC20", testInfo.l1Semvers.OptimismMintableERC20, chainState.OptimismMintableERC20FactoryProxyAddress},
}
versionArgs, err := versionFunc.EncodeArgs()
require.NoError(t, err)
for _, tt := range versionTests {
ret, err := l1Client.CallContract(ctx, ethereum.CallMsg{
To: &tt.addr,
Data: versionArgs,
}, nil)
require.NoError(t, err)
var actVersion string
require.NoError(t, versionFunc.DecodeReturns(ret, &actVersion))
require.Equal(t, tt.expVersion, actVersion, "unexpected version for %s", tt.name)
}
superchain, err := standard.SuperchainFor(l1ChainIDBig.Uint64())
require.NoError(t, err)
managerOwner, err := standard.SuperchainProxyAdminAddrFor(l1ChainIDBig.Uint64())
require.NoError(t, err)
superchainTests := []struct {
name string
expAddr common.Address
actAddr common.Address
}{
{"ProxyAdmin", managerOwner, st.SuperchainDeployment.ProxyAdminAddress},
{"SuperchainConfig", common.Address(*superchain.Config.SuperchainConfigAddr), st.SuperchainDeployment.SuperchainConfigProxyAddress},
{"ProtocolVersions", common.Address(*superchain.Config.ProtocolVersionsAddr), st.SuperchainDeployment.ProtocolVersionsProxyAddress},
}
for _, tt := range superchainTests {
require.Equal(t, tt.expAddr, tt.actAddr, "unexpected address for %s", tt.name)
}
artifactsFSL2, cleanupL2, err := artifacts.Download(
ctx,
intent.L2ContractsLocator,
artifacts.LogProgressor(lgr),
)
require.NoError(t, err)
t.Cleanup(func() {
require.NoError(t, cleanupL2())
})
chainIntent := intent.Chains[0]
semvers, err := inspect.L2Semvers(inspect.L2SemversConfig{
Lgr: lgr,
Artifacts: artifactsFSL2,
ChainState: chainState,
})
require.NoError(t, err)
require.EqualValues(t, testInfo.l2Semvers, semvers)
f, err := os.Open(fmt.Sprintf("./testdata/%s", testInfo.l2AllocsFile))
require.NoError(t, err)
defer f.Close()
gzr, err := gzip.NewReader(f)
require.NoError(t, err)
defer gzr.Close()
dec := json.NewDecoder(bufio.NewReader(gzr))
var expAllocs types.GenesisAlloc
require.NoError(t, dec.Decode(&expAllocs))
type storageCheckerFunc func(addr common.Address, actStorage map[common.Hash]common.Hash)
storageDiff := func(addr common.Address, expStorage, actStorage map[common.Hash]common.Hash) {
require.EqualValues(t, expStorage, actStorage, "storage for %s differs", addr)
}
defaultStorageChecker := func(addr common.Address, actStorage map[common.Hash]common.Hash) {
storageDiff(addr, expAllocs[addr].Storage, actStorage)
}
overrideStorageChecker := func(addr common.Address, actStorage, overrides map[common.Hash]common.Hash) {
expStorage := make(map[common.Hash]common.Hash)
maps.Copy(expStorage, expAllocs[addr].Storage)
maps.Copy(expStorage, overrides)
storageDiff(addr, expStorage, actStorage)
}
storageCheckers := map[common.Address]storageCheckerFunc{
predeploys.L2CrossDomainMessengerAddr: func(addr common.Address, actStorage map[common.Hash]common.Hash) {
overrideStorageChecker(addr, actStorage, map[common.Hash]common.Hash{
{31: 0xcf}: common.BytesToHash(chainState.L1CrossDomainMessengerProxyAddress.Bytes()),
})
},
predeploys.L2StandardBridgeAddr: func(addr common.Address, actStorage map[common.Hash]common.Hash) {
overrideStorageChecker(addr, actStorage, map[common.Hash]common.Hash{
{31: 0x04}: common.BytesToHash(chainState.L1StandardBridgeProxyAddress.Bytes()),
})
},
predeploys.L2ERC721BridgeAddr: func(addr common.Address, actStorage map[common.Hash]common.Hash) {
overrideStorageChecker(addr, actStorage, map[common.Hash]common.Hash{
{31: 0x02}: common.BytesToHash(chainState.L1ERC721BridgeProxyAddress.Bytes()),
})
},
predeploys.ProxyAdminAddr: func(addr common.Address, actStorage map[common.Hash]common.Hash) {
overrideStorageChecker(addr, actStorage, map[common.Hash]common.Hash{
{}: common.BytesToHash(intent.Chains[0].Roles.L2ProxyAdminOwner.Bytes()),
})
},
// The ProxyAdmin owner is also set on the ProxyAdmin contract's implementation address, see
// L2Genesis.s.sol line 292.
common.HexToAddress("0xc0d3c0d3c0d3c0d3c0d3c0d3c0d3c0d3c0d30018"): func(addr common.Address, actStorage map[common.Hash]common.Hash) {
overrideStorageChecker(addr, actStorage, map[common.Hash]common.Hash{
{}: common.BytesToHash(chainIntent.Roles.L2ProxyAdminOwner.Bytes()),
})
},
}
//Use a custom equality function to compare the genesis allocs
//because the reflect-based one is really slow
actAllocs := st.Chains[0].Allocs.Data.Accounts
for addr, expAcc := range expAllocs {
actAcc, ok := actAllocs[addr]
require.True(t, ok)
require.True(t, expAcc.Balance.Cmp(actAcc.Balance) == 0, "balance for %s differs", addr)
require.Equal(t, expAcc.Nonce, actAcc.Nonce, "nonce for %s differs", addr)
require.Equal(t, hex.EncodeToString(expAllocs[addr].Code), hex.EncodeToString(actAcc.Code), "code for %s differs", addr)
storageChecker, ok := storageCheckers[addr]
if !ok {
storageChecker = defaultStorageChecker
}
storageChecker(addr, actAcc.Storage)
}
for addr := range actAllocs {
if _, ok := expAllocs[addr]; ok {
continue
}
t.Logf("unexpected account: %s", addr.Hex())
}
}
func TestGlobalOverrides(t *testing.T) { func TestGlobalOverrides(t *testing.T) {
op_e2e.InitParallel(t) op_e2e.InitParallel(t)
......
...@@ -9,46 +9,23 @@ import ( ...@@ -9,46 +9,23 @@ import (
) )
var anchorRootFunc = w3.MustNewFunc(` var anchorRootFunc = w3.MustNewFunc(`
dummy((uint32 gameType, (bytes32 root, uint256 l2BlockNumber) outputRoot)[] roots) dummy((bytes32 root, uint256 l2BlockNumber) outputRoot)
`, "") `, "")
type StartingAnchorRoot struct { type StartingAnchorRoot struct {
GameType uint32
Root common.Hash Root common.Hash
L2BlockNumber *big.Int L2BlockNumber *big.Int
} }
var DefaultStartingAnchorRoot = StartingAnchorRoot{ var DefaultStartingAnchorRoot = StartingAnchorRoot{
GameType: 1,
Root: common.Hash{0xde, 0xad}, Root: common.Hash{0xde, 0xad},
L2BlockNumber: common.Big0, L2BlockNumber: common.Big0,
} }
type encodingStartingAnchorRoot struct { func EncodeStartingAnchorRoot(root StartingAnchorRoot) ([]byte, error) {
GameType uint32 encoded, err := anchorRootFunc.EncodeArgs(root)
OutputRoot struct {
Root common.Hash
L2BlockNumber *big.Int
}
}
func EncodeStartingAnchorRoots(roots []StartingAnchorRoot) ([]byte, error) {
args := make([]encodingStartingAnchorRoot, len(roots))
for i, root := range roots {
args[i] = encodingStartingAnchorRoot{
GameType: root.GameType,
OutputRoot: struct {
Root common.Hash
L2BlockNumber *big.Int
}{
Root: root.Root,
L2BlockNumber: root.L2BlockNumber,
},
}
}
encoded, err := anchorRootFunc.EncodeArgs(args)
if err != nil { if err != nil {
return nil, fmt.Errorf("error encoding anchor roots: %w", err) return nil, fmt.Errorf("error encoding anchor root: %w", err)
} }
// Chop off the function selector since w3 can't serialize structs directly // Chop off the function selector since w3 can't serialize structs directly
return encoded[4:], nil return encoded[4:], nil
......
...@@ -9,34 +9,22 @@ import ( ...@@ -9,34 +9,22 @@ import (
) )
func TestEncodeStartingAnchorRoots(t *testing.T) { func TestEncodeStartingAnchorRoots(t *testing.T) {
encoded, err := EncodeStartingAnchorRoots([]StartingAnchorRoot{ encoded, err := EncodeStartingAnchorRoot(DefaultStartingAnchorRoot)
DefaultStartingAnchorRoot,
})
require.NoError(t, err) require.NoError(t, err)
require.EqualValues(t, PermissionedGameStartingAnchorRoots, encoded) require.EqualValues(t, PermissionedGameStartingAnchorRoot, encoded)
encoded, err = EncodeStartingAnchorRoots([]StartingAnchorRoot{ encoded, err = EncodeStartingAnchorRoot(StartingAnchorRoot{
{ Root: common.Hash{0xde, 0xad, 0xbe, 0xef},
GameType: 0, L2BlockNumber: big.NewInt(9),
L2BlockNumber: common.Big0,
},
{
GameType: 1,
Root: common.Hash{0xde, 0xad},
L2BlockNumber: big.NewInt(0),
},
}) })
require.NoError(t, err) require.NoError(t, err)
require.EqualValues(t, require.EqualValues(t,
common.Hex2Bytes( []byte{
"0000000000000000000000000000000000000000000000000000000000000020"+ 0xde, 0xad, 0xbe, 0xef, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
"0000000000000000000000000000000000000000000000000000000000000002"+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
"0000000000000000000000000000000000000000000000000000000000000000"+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
"0000000000000000000000000000000000000000000000000000000000000000"+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x09,
"0000000000000000000000000000000000000000000000000000000000000000"+ },
"0000000000000000000000000000000000000000000000000000000000000001"+
"dead000000000000000000000000000000000000000000000000000000000000"+
"0000000000000000000000000000000000000000000000000000000000000000"),
encoded, encoded,
) )
} }
...@@ -39,6 +39,7 @@ type DeployImplementationsOutput struct { ...@@ -39,6 +39,7 @@ type DeployImplementationsOutput struct {
L1StandardBridgeImpl common.Address L1StandardBridgeImpl common.Address
OptimismMintableERC20FactoryImpl common.Address OptimismMintableERC20FactoryImpl common.Address
DisputeGameFactoryImpl common.Address DisputeGameFactoryImpl common.Address
AnchorStateRegistryImpl common.Address
} }
func (output *DeployImplementationsOutput) CheckOutput(input common.Address) error { func (output *DeployImplementationsOutput) CheckOutput(input common.Address) error {
......
...@@ -9,13 +9,16 @@ import ( ...@@ -9,13 +9,16 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
) )
// PermissionedGameStartingAnchorRoots is a root of bytes32(hex"dead") for the permissioned game at block 0, // PermissionedGameStartingAnchorRoot is a root of bytes32(hex"dead") for the permissioned game at block 0,
// and no root for the permissionless game. // and no root for the permissionless game.
var PermissionedGameStartingAnchorRoots = []byte{ var PermissionedGameStartingAnchorRoot = []byte{
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xde, 0xad, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0xad, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
} }
type DeployOPChainInputV160 struct { type DeployOPChainInput struct {
OpChainProxyAdminOwner common.Address OpChainProxyAdminOwner common.Address
SystemConfigOwner common.Address SystemConfigOwner common.Address
Batcher common.Address Batcher common.Address
...@@ -36,14 +39,17 @@ type DeployOPChainInputV160 struct { ...@@ -36,14 +39,17 @@ type DeployOPChainInputV160 struct {
DisputeSplitDepth uint64 DisputeSplitDepth uint64
DisputeClockExtension uint64 DisputeClockExtension uint64
DisputeMaxClockDuration uint64 DisputeMaxClockDuration uint64
StartingAnchorRoots []byte
AllowCustomDisputeParameters bool AllowCustomDisputeParameters bool
} }
func (input *DeployOPChainInputV160) InputSet() bool { func (input *DeployOPChainInput) InputSet() bool {
return true return true
} }
func (input *DeployOPChainInput) StartingAnchorRoot() []byte {
return PermissionedGameStartingAnchorRoot
}
type DeployOPChainOutput struct { type DeployOPChainOutput struct {
OpChainProxyAdmin common.Address OpChainProxyAdmin common.Address
AddressManager common.Address AddressManager common.Address
...@@ -71,8 +77,8 @@ type DeployOPChainScript struct { ...@@ -71,8 +77,8 @@ type DeployOPChainScript struct {
Run func(input, output common.Address) error Run func(input, output common.Address) error
} }
func DeployOPChainV160(host *script.Host, input DeployOPChainInputV160) (DeployOPChainOutput, error) { func DeployOPChain(host *script.Host, input DeployOPChainInput) (DeployOPChainOutput, error) {
return RunScriptSingle[DeployOPChainInputV160, DeployOPChainOutput](host, input, "DeployOPChain.s.sol", "DeployOPChain") return RunScriptSingle[DeployOPChainInput, DeployOPChainOutput](host, input, "DeployOPChain.s.sol", "DeployOPChain")
} }
type ReadImplementationAddressesInput struct { type ReadImplementationAddressesInput struct {
......
...@@ -17,7 +17,6 @@ type DeployOPCMInput struct { ...@@ -17,7 +17,6 @@ type DeployOPCMInput struct {
ProxyAdminBlueprint common.Address ProxyAdminBlueprint common.Address
L1ChugSplashProxyBlueprint common.Address L1ChugSplashProxyBlueprint common.Address
ResolvedDelegateProxyBlueprint common.Address ResolvedDelegateProxyBlueprint common.Address
AnchorStateRegistryBlueprint common.Address
PermissionedDisputeGame1Blueprint common.Address PermissionedDisputeGame1Blueprint common.Address
PermissionedDisputeGame2Blueprint common.Address PermissionedDisputeGame2Blueprint common.Address
...@@ -28,6 +27,7 @@ type DeployOPCMInput struct { ...@@ -28,6 +27,7 @@ type DeployOPCMInput struct {
L1CrossDomainMessengerImpl common.Address L1CrossDomainMessengerImpl common.Address
L1StandardBridgeImpl common.Address L1StandardBridgeImpl common.Address
DisputeGameFactoryImpl common.Address DisputeGameFactoryImpl common.Address
AnchorStateRegistryImpl common.Address
DelayedWETHImpl common.Address DelayedWETHImpl common.Address
MipsImpl common.Address MipsImpl common.Address
} }
......
...@@ -75,6 +75,7 @@ func DeployImplementations(env *Env, intent *state.Intent, st *state.State) erro ...@@ -75,6 +75,7 @@ func DeployImplementations(env *Env, intent *state.Intent, st *state.State) erro
L1StandardBridgeImplAddress: dio.L1StandardBridgeImpl, L1StandardBridgeImplAddress: dio.L1StandardBridgeImpl,
OptimismMintableERC20FactoryImplAddress: dio.OptimismMintableERC20FactoryImpl, OptimismMintableERC20FactoryImplAddress: dio.OptimismMintableERC20FactoryImpl,
DisputeGameFactoryImplAddress: dio.DisputeGameFactoryImpl, DisputeGameFactoryImplAddress: dio.DisputeGameFactoryImpl,
AnchorStateRegistryImplAddress: dio.AnchorStateRegistryImpl,
} }
return nil return nil
......
...@@ -27,12 +27,12 @@ func DeployOPChain(env *Env, intent *state.Intent, st *state.State, chainID comm ...@@ -27,12 +27,12 @@ func DeployOPChain(env *Env, intent *state.Intent, st *state.State, chainID comm
var dco opcm.DeployOPChainOutput var dco opcm.DeployOPChainOutput
lgr.Info("deploying OP chain using local allocs", "id", chainID.Hex()) lgr.Info("deploying OP chain using local allocs", "id", chainID.Hex())
dci, err := makeDCIV160(intent, thisIntent, chainID, st) dci, err := makeDCI(intent, thisIntent, chainID, st)
if err != nil { if err != nil {
return fmt.Errorf("error making deploy OP chain input: %w", err) return fmt.Errorf("error making deploy OP chain input: %w", err)
} }
dco, err = opcm.DeployOPChainV160(env.L1ScriptHost, dci) dco, err = opcm.DeployOPChain(env.L1ScriptHost, dci)
if err != nil { if err != nil {
return fmt.Errorf("error deploying OP chain: %w", err) return fmt.Errorf("error deploying OP chain: %w", err)
} }
...@@ -70,7 +70,7 @@ func DeployOPChain(env *Env, intent *state.Intent, st *state.State, chainID comm ...@@ -70,7 +70,7 @@ func DeployOPChain(env *Env, intent *state.Intent, st *state.State, chainID comm
return nil return nil
} }
func makeDCIV160(intent *state.Intent, thisIntent *state.ChainIntent, chainID common.Hash, st *state.State) (opcm.DeployOPChainInputV160, error) { func makeDCI(intent *state.Intent, thisIntent *state.ChainIntent, chainID common.Hash, st *state.State) (opcm.DeployOPChainInput, error) {
proofParams, err := jsonutil.MergeJSON( proofParams, err := jsonutil.MergeJSON(
state.ChainProofParams{ state.ChainProofParams{
DisputeGameType: standard.DisputeGameType, DisputeGameType: standard.DisputeGameType,
...@@ -84,31 +84,10 @@ func makeDCIV160(intent *state.Intent, thisIntent *state.ChainIntent, chainID co ...@@ -84,31 +84,10 @@ func makeDCIV160(intent *state.Intent, thisIntent *state.ChainIntent, chainID co
thisIntent.DeployOverrides, thisIntent.DeployOverrides,
) )
if err != nil { if err != nil {
return opcm.DeployOPChainInputV160{}, fmt.Errorf("error merging proof params from overrides: %w", err) return opcm.DeployOPChainInput{}, fmt.Errorf("error merging proof params from overrides: %w", err)
} }
startingAnchorRoots := opcm.PermissionedGameStartingAnchorRoots return opcm.DeployOPChainInput{
if len(thisIntent.AdditionalDisputeGames) > 0 {
anchorRoots := []opcm.StartingAnchorRoot{
opcm.DefaultStartingAnchorRoot,
}
for _, game := range thisIntent.AdditionalDisputeGames {
anchorRoots = append(anchorRoots, opcm.StartingAnchorRoot{
GameType: game.DisputeGameType,
Root: game.StartingAnchorRoot,
L2BlockNumber: common.Big0,
})
}
encoded, err := opcm.EncodeStartingAnchorRoots(anchorRoots)
if err != nil {
return opcm.DeployOPChainInputV160{}, fmt.Errorf("error encoding starting anchor roots: %w", err)
}
startingAnchorRoots = encoded
}
return opcm.DeployOPChainInputV160{
OpChainProxyAdminOwner: thisIntent.Roles.L1ProxyAdminOwner, OpChainProxyAdminOwner: thisIntent.Roles.L1ProxyAdminOwner,
SystemConfigOwner: thisIntent.Roles.SystemConfigOwner, SystemConfigOwner: thisIntent.Roles.SystemConfigOwner,
Batcher: thisIntent.Roles.Batcher, Batcher: thisIntent.Roles.Batcher,
...@@ -128,7 +107,6 @@ func makeDCIV160(intent *state.Intent, thisIntent *state.ChainIntent, chainID co ...@@ -128,7 +107,6 @@ func makeDCIV160(intent *state.Intent, thisIntent *state.ChainIntent, chainID co
DisputeClockExtension: proofParams.DisputeClockExtension, // 3 hours (input in seconds) DisputeClockExtension: proofParams.DisputeClockExtension, // 3 hours (input in seconds)
DisputeMaxClockDuration: proofParams.DisputeMaxClockDuration, // 3.5 days (input in seconds) DisputeMaxClockDuration: proofParams.DisputeMaxClockDuration, // 3.5 days (input in seconds)
AllowCustomDisputeParameters: proofParams.DangerouslyAllowCustomDisputeParameters, AllowCustomDisputeParameters: proofParams.DangerouslyAllowCustomDisputeParameters,
StartingAnchorRoots: startingAnchorRoots,
}, nil }, nil
} }
......
...@@ -33,7 +33,6 @@ type AdditionalDisputeGame struct { ...@@ -33,7 +33,6 @@ type AdditionalDisputeGame struct {
OracleMinProposalSize uint64 OracleMinProposalSize uint64
OracleChallengePeriodSeconds uint64 OracleChallengePeriodSeconds uint64
MakeRespected bool MakeRespected bool
StartingAnchorRoot common.Hash
} }
type ChainIntent struct { type ChainIntent struct {
......
...@@ -81,6 +81,7 @@ type ImplementationsDeployment struct { ...@@ -81,6 +81,7 @@ type ImplementationsDeployment struct {
L1StandardBridgeImplAddress common.Address `json:"l1StandardBridgeImplAddress"` L1StandardBridgeImplAddress common.Address `json:"l1StandardBridgeImplAddress"`
OptimismMintableERC20FactoryImplAddress common.Address `json:"optimismMintableERC20FactoryImplAddress"` OptimismMintableERC20FactoryImplAddress common.Address `json:"optimismMintableERC20FactoryImplAddress"`
DisputeGameFactoryImplAddress common.Address `json:"disputeGameFactoryImplAddress"` DisputeGameFactoryImplAddress common.Address `json:"disputeGameFactoryImplAddress"`
AnchorStateRegistryImplAddress common.Address `json:"anchorStateRegistryImplAddress"`
} }
type AdditionalDisputeGameState struct { type AdditionalDisputeGameState struct {
......
...@@ -443,7 +443,6 @@ func defaultIntent(root string, loc *artifacts.Locator, deployer common.Address, ...@@ -443,7 +443,6 @@ func defaultIntent(root string, loc *artifacts.Locator, deployer common.Address,
OracleMinProposalSize: 10000, OracleMinProposalSize: 10000,
OracleChallengePeriodSeconds: 0, OracleChallengePeriodSeconds: 0,
MakeRespected: true, MakeRespected: true,
StartingAnchorRoot: genesisOutputRoot,
}, },
{ {
ChainProofParams: state.ChainProofParams{ ChainProofParams: state.ChainProofParams{
...@@ -456,7 +455,6 @@ func defaultIntent(root string, loc *artifacts.Locator, deployer common.Address, ...@@ -456,7 +455,6 @@ func defaultIntent(root string, loc *artifacts.Locator, deployer common.Address,
DisputeMaxClockDuration: 1200, DisputeMaxClockDuration: 1200,
}, },
VMType: state.VMTypeAlphabet, VMType: state.VMTypeAlphabet,
StartingAnchorRoot: genesisOutputRoot,
}, },
{ {
ChainProofParams: state.ChainProofParams{ ChainProofParams: state.ChainProofParams{
...@@ -468,7 +466,6 @@ func defaultIntent(root string, loc *artifacts.Locator, deployer common.Address, ...@@ -468,7 +466,6 @@ func defaultIntent(root string, loc *artifacts.Locator, deployer common.Address,
DisputeMaxClockDuration: 1200, DisputeMaxClockDuration: 1200,
}, },
VMType: cannonVMType(allocType), VMType: cannonVMType(allocType),
StartingAnchorRoot: genesisOutputRoot,
}, },
}, },
}, },
......
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity ^0.8.0; pragma solidity ^0.8.0;
import { IDisputeGame } from "interfaces/dispute/IDisputeGame.sol";
import { IFaultDisputeGame } from "interfaces/dispute/IFaultDisputeGame.sol"; import { IFaultDisputeGame } from "interfaces/dispute/IFaultDisputeGame.sol";
import { IDisputeGameFactory } from "interfaces/dispute/IDisputeGameFactory.sol"; import { IDisputeGameFactory } from "interfaces/dispute/IDisputeGameFactory.sol";
import { ISuperchainConfig } from "interfaces/L1/ISuperchainConfig.sol"; import { ISuperchainConfig } from "interfaces/L1/ISuperchainConfig.sol";
import { IOptimismPortal2 } from "interfaces/L1/IOptimismPortal2.sol";
import { GameType, Hash, OutputRoot } from "src/dispute/lib/Types.sol"; import { GameType, Hash, OutputRoot } from "src/dispute/lib/Types.sol";
interface IAnchorStateRegistry { interface IAnchorStateRegistry {
struct StartingAnchorRoot { error AnchorStateRegistry_Unauthorized();
GameType gameType; error AnchorStateRegistry_ImproperAnchorGame();
OutputRoot outputRoot; error AnchorStateRegistry_InvalidAnchorGame();
}
error InvalidGameStatus();
error Unauthorized();
error UnregisteredGame();
event AnchorNotUpdated(IFaultDisputeGame indexed game);
event AnchorUpdated(IFaultDisputeGame indexed game);
event Initialized(uint8 version); event Initialized(uint8 version);
function anchors(GameType) external view returns (Hash root, uint256 l2BlockNumber); // nosemgrep function anchorGame() external view returns (IFaultDisputeGame);
function anchors(GameType) external view returns (Hash, uint256);
function getAnchorRoot() external view returns (Hash, uint256);
function disputeGameFactory() external view returns (IDisputeGameFactory); function disputeGameFactory() external view returns (IDisputeGameFactory);
function initialize( function initialize(ISuperchainConfig _superchainConfig, IDisputeGameFactory _disputeGameFactory, IOptimismPortal2 _portal, OutputRoot memory _startingAnchorRoot) external;
StartingAnchorRoot[] memory _startingAnchorRoots, function isGameRegistered(IDisputeGame _game) external view returns (bool);
ISuperchainConfig _superchainConfig function isGameBlacklisted(IDisputeGame _game) external view returns (bool);
) function isGameRespected(IDisputeGame _game) external view returns (bool);
external; function isGameRetired(IDisputeGame _game) external view returns (bool);
function isGameProper(IDisputeGame _game) external view returns (bool);
function portal() external view returns (IOptimismPortal2);
function setAnchorState(IFaultDisputeGame _game) external; function setAnchorState(IFaultDisputeGame _game) external;
function superchainConfig() external view returns (ISuperchainConfig); function superchainConfig() external view returns (ISuperchainConfig);
function tryUpdateAnchorState() external; function tryUpdateAnchorState() external;
function version() external view returns (string memory); function version() external view returns (string memory);
function __constructor__(IDisputeGameFactory _disputeGameFactory) external; function __constructor__() external;
} }
...@@ -73,11 +73,13 @@ test-upgrade *ARGS: build-go-ffi ...@@ -73,11 +73,13 @@ test-upgrade *ARGS: build-go-ffi
#!/bin/bash #!/bin/bash
echo "Running upgrade tests at block $pinnedBlockNumber" echo "Running upgrade tests at block $pinnedBlockNumber"
export FORK_BLOCK_NUMBER=$pinnedBlockNumber export FORK_BLOCK_NUMBER=$pinnedBlockNumber
export NO_MATCH_CONTRACTS="OptimismPortal2WithMockERC20_Test|OptimismPortal2_FinalizeWithdrawal_Test|AnchorStateRegistry_Initialize_Test|AnchorStateRegistry_TryUpdateAnchorState_Test|FaultDisputeGame_Test|FaultDispute_1v1_Actors_Test" export NO_MATCH_CONTRACTS="OptimismPortal2WithMockERC20_Test|OptimismPortal2_FinalizeWithdrawal_Test|'AnchorStateRegistry_*'|FaultDisputeGame_Test|FaultDispute_1v1_Actors_Test"
export NO_MATCH_PATHS="test/dispute/AnchorStateRegistry.t.sol"
FORK_RPC_URL=$ETH_RPC_URL \ FORK_RPC_URL=$ETH_RPC_URL \
FORK_TEST=true \ FORK_TEST=true \
forge test --match-path "test/{L1,dispute}/**" \ forge test --match-path "test/{L1,dispute}/**" \
--no-match-contract "$NO_MATCH_CONTRACTS" \ --no-match-contract "$NO_MATCH_CONTRACTS" \
--no-match-path "$NO_MATCH_PATHS" \
{{ARGS}} {{ARGS}}
test-upgrade-rerun *ARGS: build-go-ffi test-upgrade-rerun *ARGS: build-go-ffi
......
...@@ -529,10 +529,6 @@ library ChainAssertions { ...@@ -529,10 +529,6 @@ library ChainAssertions {
Blueprint.parseBlueprintPreamble(address(blueprints.resolvedDelegateProxy).code); Blueprint.parseBlueprintPreamble(address(blueprints.resolvedDelegateProxy).code);
require(keccak256(rdProxyPreamble.initcode) == keccak256(vm.getCode("ResolvedDelegateProxy")), "CHECK-OPCM-180"); require(keccak256(rdProxyPreamble.initcode) == keccak256(vm.getCode("ResolvedDelegateProxy")), "CHECK-OPCM-180");
Blueprint.Preamble memory asrPreamble =
Blueprint.parseBlueprintPreamble(address(blueprints.anchorStateRegistry).code);
require(keccak256(asrPreamble.initcode) == keccak256(vm.getCode("AnchorStateRegistry")), "CHECK-OPCM-190");
Blueprint.Preamble memory pdg1Preamble = Blueprint.Preamble memory pdg1Preamble =
Blueprint.parseBlueprintPreamble(address(blueprints.permissionedDisputeGame1).code); Blueprint.parseBlueprintPreamble(address(blueprints.permissionedDisputeGame1).code);
Blueprint.Preamble memory pdg2Preamble = Blueprint.Preamble memory pdg2Preamble =
......
...@@ -380,7 +380,6 @@ contract Deploy is Deployer { ...@@ -380,7 +380,6 @@ contract Deploy is Deployer {
artifacts.save("DisputeGameFactoryProxy", address(deployOutput.disputeGameFactoryProxy)); artifacts.save("DisputeGameFactoryProxy", address(deployOutput.disputeGameFactoryProxy));
artifacts.save("PermissionedDelayedWETHProxy", address(deployOutput.delayedWETHPermissionedGameProxy)); artifacts.save("PermissionedDelayedWETHProxy", address(deployOutput.delayedWETHPermissionedGameProxy));
artifacts.save("AnchorStateRegistryProxy", address(deployOutput.anchorStateRegistryProxy)); artifacts.save("AnchorStateRegistryProxy", address(deployOutput.anchorStateRegistryProxy));
artifacts.save("AnchorStateRegistryImpl", address(deployOutput.anchorStateRegistryImpl));
artifacts.save("PermissionedDisputeGame", address(deployOutput.permissionedDisputeGame)); artifacts.save("PermissionedDisputeGame", address(deployOutput.permissionedDisputeGame));
artifacts.save("OptimismPortalProxy", address(deployOutput.optimismPortalProxy)); artifacts.save("OptimismPortalProxy", address(deployOutput.optimismPortalProxy));
artifacts.save("OptimismPortal2Proxy", address(deployOutput.optimismPortalProxy)); artifacts.save("OptimismPortal2Proxy", address(deployOutput.optimismPortalProxy));
...@@ -854,24 +853,6 @@ contract Deploy is Deployer { ...@@ -854,24 +853,6 @@ contract Deploy is Deployer {
/// @notice Get the DeployInput struct to use for testing /// @notice Get the DeployInput struct to use for testing
function getDeployInput() public view returns (OPContractsManager.DeployInput memory) { function getDeployInput() public view returns (OPContractsManager.DeployInput memory) {
OutputRoot memory testOutputRoot = OutputRoot({
root: Hash.wrap(cfg.faultGameGenesisOutputRoot()),
l2BlockNumber: cfg.faultGameGenesisBlock()
});
IAnchorStateRegistry.StartingAnchorRoot[] memory startingAnchorRoots =
new IAnchorStateRegistry.StartingAnchorRoot[](5);
startingAnchorRoots[0] =
IAnchorStateRegistry.StartingAnchorRoot({ gameType: GameTypes.CANNON, outputRoot: testOutputRoot });
startingAnchorRoots[1] = IAnchorStateRegistry.StartingAnchorRoot({
gameType: GameTypes.PERMISSIONED_CANNON,
outputRoot: testOutputRoot
});
startingAnchorRoots[2] =
IAnchorStateRegistry.StartingAnchorRoot({ gameType: GameTypes.ASTERISC, outputRoot: testOutputRoot });
startingAnchorRoots[3] =
IAnchorStateRegistry.StartingAnchorRoot({ gameType: GameTypes.FAST, outputRoot: testOutputRoot });
startingAnchorRoots[4] =
IAnchorStateRegistry.StartingAnchorRoot({ gameType: GameTypes.ALPHABET, outputRoot: testOutputRoot });
string memory saltMixer = "salt mixer"; string memory saltMixer = "salt mixer";
return OPContractsManager.DeployInput({ return OPContractsManager.DeployInput({
roles: OPContractsManager.Roles({ roles: OPContractsManager.Roles({
...@@ -885,7 +866,9 @@ contract Deploy is Deployer { ...@@ -885,7 +866,9 @@ contract Deploy is Deployer {
basefeeScalar: cfg.basefeeScalar(), basefeeScalar: cfg.basefeeScalar(),
blobBasefeeScalar: cfg.blobbasefeeScalar(), blobBasefeeScalar: cfg.blobbasefeeScalar(),
l2ChainId: cfg.l2ChainID(), l2ChainId: cfg.l2ChainID(),
startingAnchorRoots: abi.encode(startingAnchorRoots), startingAnchorRoot: abi.encode(
OutputRoot({ root: Hash.wrap(cfg.faultGameGenesisOutputRoot()), l2BlockNumber: cfg.faultGameGenesisBlock() })
),
saltMixer: saltMixer, saltMixer: saltMixer,
gasLimit: uint64(cfg.l2GenesisBlockGasLimit()), gasLimit: uint64(cfg.l2GenesisBlockGasLimit()),
disputeGameType: GameTypes.PERMISSIONED_CANNON, disputeGameType: GameTypes.PERMISSIONED_CANNON,
......
...@@ -13,7 +13,7 @@ import { IDelayedWETH } from "interfaces/dispute/IDelayedWETH.sol"; ...@@ -13,7 +13,7 @@ import { IDelayedWETH } from "interfaces/dispute/IDelayedWETH.sol";
import { IPreimageOracle } from "interfaces/cannon/IPreimageOracle.sol"; import { IPreimageOracle } from "interfaces/cannon/IPreimageOracle.sol";
import { IMIPS } from "interfaces/cannon/IMIPS.sol"; import { IMIPS } from "interfaces/cannon/IMIPS.sol";
import { IDisputeGameFactory } from "interfaces/dispute/IDisputeGameFactory.sol"; import { IDisputeGameFactory } from "interfaces/dispute/IDisputeGameFactory.sol";
import { IAnchorStateRegistry } from "interfaces/dispute/IAnchorStateRegistry.sol";
import { OPContractsManager } from "src/L1/OPContractsManager.sol"; import { OPContractsManager } from "src/L1/OPContractsManager.sol";
import { IOptimismPortal2 } from "interfaces/L1/IOptimismPortal2.sol"; import { IOptimismPortal2 } from "interfaces/L1/IOptimismPortal2.sol";
import { ISystemConfig } from "interfaces/L1/ISystemConfig.sol"; import { ISystemConfig } from "interfaces/L1/ISystemConfig.sol";
...@@ -142,6 +142,7 @@ contract DeployImplementationsOutput is BaseDeployIO { ...@@ -142,6 +142,7 @@ contract DeployImplementationsOutput is BaseDeployIO {
IL1StandardBridge internal _l1StandardBridgeImpl; IL1StandardBridge internal _l1StandardBridgeImpl;
IOptimismMintableERC20Factory internal _optimismMintableERC20FactoryImpl; IOptimismMintableERC20Factory internal _optimismMintableERC20FactoryImpl;
IDisputeGameFactory internal _disputeGameFactoryImpl; IDisputeGameFactory internal _disputeGameFactoryImpl;
IAnchorStateRegistry internal _anchorStateRegistryImpl;
function set(bytes4 _sel, address _addr) public { function set(bytes4 _sel, address _addr) public {
require(_addr != address(0), "DeployImplementationsOutput: cannot set zero address"); require(_addr != address(0), "DeployImplementationsOutput: cannot set zero address");
...@@ -158,6 +159,7 @@ contract DeployImplementationsOutput is BaseDeployIO { ...@@ -158,6 +159,7 @@ contract DeployImplementationsOutput is BaseDeployIO {
else if (_sel == this.l1StandardBridgeImpl.selector) _l1StandardBridgeImpl = IL1StandardBridge(payable(_addr)); else if (_sel == this.l1StandardBridgeImpl.selector) _l1StandardBridgeImpl = IL1StandardBridge(payable(_addr));
else if (_sel == this.optimismMintableERC20FactoryImpl.selector) _optimismMintableERC20FactoryImpl = IOptimismMintableERC20Factory(_addr); else if (_sel == this.optimismMintableERC20FactoryImpl.selector) _optimismMintableERC20FactoryImpl = IOptimismMintableERC20Factory(_addr);
else if (_sel == this.disputeGameFactoryImpl.selector) _disputeGameFactoryImpl = IDisputeGameFactory(_addr); else if (_sel == this.disputeGameFactoryImpl.selector) _disputeGameFactoryImpl = IDisputeGameFactory(_addr);
else if (_sel == this.anchorStateRegistryImpl.selector) _anchorStateRegistryImpl = IAnchorStateRegistry(_addr);
else revert("DeployImplementationsOutput: unknown selector"); else revert("DeployImplementationsOutput: unknown selector");
// forgefmt: disable-end // forgefmt: disable-end
} }
...@@ -179,7 +181,8 @@ contract DeployImplementationsOutput is BaseDeployIO { ...@@ -179,7 +181,8 @@ contract DeployImplementationsOutput is BaseDeployIO {
address(this.l1ERC721BridgeImpl()), address(this.l1ERC721BridgeImpl()),
address(this.l1StandardBridgeImpl()), address(this.l1StandardBridgeImpl()),
address(this.optimismMintableERC20FactoryImpl()), address(this.optimismMintableERC20FactoryImpl()),
address(this.disputeGameFactoryImpl()) address(this.disputeGameFactoryImpl()),
address(this.anchorStateRegistryImpl())
); );
DeployUtils.assertValidContractAddresses(Solarray.extend(addrs1, addrs2)); DeployUtils.assertValidContractAddresses(Solarray.extend(addrs1, addrs2));
...@@ -242,10 +245,16 @@ contract DeployImplementationsOutput is BaseDeployIO { ...@@ -242,10 +245,16 @@ contract DeployImplementationsOutput is BaseDeployIO {
return _disputeGameFactoryImpl; return _disputeGameFactoryImpl;
} }
function anchorStateRegistryImpl() public view returns (IAnchorStateRegistry) {
DeployUtils.assertValidContractAddress(address(_anchorStateRegistryImpl));
return _anchorStateRegistryImpl;
}
// -------- Deployment Assertions -------- // -------- Deployment Assertions --------
function assertValidDeploy(DeployImplementationsInput _dii) public view { function assertValidDeploy(DeployImplementationsInput _dii) public view {
assertValidDelayedWETHImpl(_dii); assertValidDelayedWETHImpl(_dii);
assertValidDisputeGameFactoryImpl(_dii); assertValidDisputeGameFactoryImpl(_dii);
assertValidAnchorStateRegistryImpl(_dii);
assertValidL1CrossDomainMessengerImpl(_dii); assertValidL1CrossDomainMessengerImpl(_dii);
assertValidL1ERC721BridgeImpl(_dii); assertValidL1ERC721BridgeImpl(_dii);
assertValidL1StandardBridgeImpl(_dii); assertValidL1StandardBridgeImpl(_dii);
...@@ -387,6 +396,12 @@ contract DeployImplementationsOutput is BaseDeployIO { ...@@ -387,6 +396,12 @@ contract DeployImplementationsOutput is BaseDeployIO {
require(address(factory.owner()) == address(0), "DG-10"); require(address(factory.owner()) == address(0), "DG-10");
} }
function assertValidAnchorStateRegistryImpl(DeployImplementationsInput) internal view {
IAnchorStateRegistry registry = anchorStateRegistryImpl();
DeployUtils.assertInitialized({ _contractAddress: address(registry), _isProxy: false, _slot: 0, _offset: 0 });
}
} }
contract DeployImplementations is Script { contract DeployImplementations is Script {
...@@ -406,6 +421,7 @@ contract DeployImplementations is Script { ...@@ -406,6 +421,7 @@ contract DeployImplementations is Script {
deployPreimageOracleSingleton(_dii, _dio); deployPreimageOracleSingleton(_dii, _dio);
deployMipsSingleton(_dii, _dio); deployMipsSingleton(_dii, _dio);
deployDisputeGameFactoryImpl(_dio); deployDisputeGameFactoryImpl(_dio);
deployAnchorStateRegistryImpl(_dio);
// Deploy the OP Contracts Manager with the new implementations set. // Deploy the OP Contracts Manager with the new implementations set.
deployOPContractsManager(_dii, _dio); deployOPContractsManager(_dii, _dio);
...@@ -438,6 +454,7 @@ contract DeployImplementations is Script { ...@@ -438,6 +454,7 @@ contract DeployImplementations is Script {
l1CrossDomainMessengerImpl: address(_dio.l1CrossDomainMessengerImpl()), l1CrossDomainMessengerImpl: address(_dio.l1CrossDomainMessengerImpl()),
l1StandardBridgeImpl: address(_dio.l1StandardBridgeImpl()), l1StandardBridgeImpl: address(_dio.l1StandardBridgeImpl()),
disputeGameFactoryImpl: address(_dio.disputeGameFactoryImpl()), disputeGameFactoryImpl: address(_dio.disputeGameFactoryImpl()),
anchorStateRegistryImpl: address(_dio.anchorStateRegistryImpl()),
delayedWETHImpl: address(_dio.delayedWETHImpl()), delayedWETHImpl: address(_dio.delayedWETHImpl()),
mipsImpl: address(_dio.mipsSingleton()) mipsImpl: address(_dio.mipsSingleton())
}); });
...@@ -481,8 +498,6 @@ contract DeployImplementations is Script { ...@@ -481,8 +498,6 @@ contract DeployImplementations is Script {
require(checkAddress == address(0), "OPCM-40"); require(checkAddress == address(0), "OPCM-40");
(blueprints.resolvedDelegateProxy, checkAddress) = DeployUtils.createDeterministicBlueprint(vm.getCode("ResolvedDelegateProxy"), _salt); (blueprints.resolvedDelegateProxy, checkAddress) = DeployUtils.createDeterministicBlueprint(vm.getCode("ResolvedDelegateProxy"), _salt);
require(checkAddress == address(0), "OPCM-50"); require(checkAddress == address(0), "OPCM-50");
(blueprints.anchorStateRegistry, checkAddress) = DeployUtils.createDeterministicBlueprint(vm.getCode("AnchorStateRegistry"), _salt);
require(checkAddress == address(0), "OPCM-60");
// The max initcode/runtimecode size is 48KB/24KB. // The max initcode/runtimecode size is 48KB/24KB.
// But for Blueprint, the initcode is stored as runtime code, that's why it's necessary to split into 2 parts. // But for Blueprint, the initcode is stored as runtime code, that's why it's necessary to split into 2 parts.
(blueprints.permissionedDisputeGame1, blueprints.permissionedDisputeGame2) = DeployUtils.createDeterministicBlueprint(vm.getCode("PermissionedDisputeGame"), _salt); (blueprints.permissionedDisputeGame1, blueprints.permissionedDisputeGame2) = DeployUtils.createDeterministicBlueprint(vm.getCode("PermissionedDisputeGame"), _salt);
...@@ -569,7 +584,7 @@ contract DeployImplementations is Script { ...@@ -569,7 +584,7 @@ contract DeployImplementations is Script {
// | Contract | Proxied | Deployment | MCP Ready | // | Contract | Proxied | Deployment | MCP Ready |
// |-------------------------|---------|-----------------------------------|------------| // |-------------------------|---------|-----------------------------------|------------|
// | DisputeGameFactory | Yes | Bespoke | Yes | // | DisputeGameFactory | Yes | Bespoke | Yes |
// | AnchorStateRegistry | Yes | Bespoke | No | // | AnchorStateRegistry | Yes | Bespoke | Yes |
// | FaultDisputeGame | No | Bespoke | No | Not yet supported by OPCM // | FaultDisputeGame | No | Bespoke | No | Not yet supported by OPCM
// | PermissionedDisputeGame | No | Bespoke | No | // | PermissionedDisputeGame | No | Bespoke | No |
// | DelayedWETH | Yes | Two bespoke (one per DisputeGame) | Yes *️⃣ | // | DelayedWETH | Yes | Two bespoke (one per DisputeGame) | Yes *️⃣ |
...@@ -586,6 +601,7 @@ contract DeployImplementations is Script { ...@@ -586,6 +601,7 @@ contract DeployImplementations is Script {
// here we deploy: // here we deploy:
// //
// - DisputeGameFactory (implementation) // - DisputeGameFactory (implementation)
// - AnchorStateRegistry (implementation)
// - OptimismPortal2 (implementation) // - OptimismPortal2 (implementation)
// - DelayedWETH (implementation) // - DelayedWETH (implementation)
// - PreimageOracle (singleton) // - PreimageOracle (singleton)
...@@ -594,7 +610,6 @@ contract DeployImplementations is Script { ...@@ -594,7 +610,6 @@ contract DeployImplementations is Script {
// For contracts which are not MCP ready neither the Proxy nor the implementation can be shared, therefore they // For contracts which are not MCP ready neither the Proxy nor the implementation can be shared, therefore they
// are deployed by `DeployOpChain.s.sol`. // are deployed by `DeployOpChain.s.sol`.
// These are: // These are:
// - AnchorStateRegistry (proxy and implementation)
// - FaultDisputeGame (not proxied) // - FaultDisputeGame (not proxied)
// - PermissionedDisputeGame (not proxied) // - PermissionedDisputeGame (not proxied)
// - DelayedWeth (proxies only) // - DelayedWeth (proxies only)
...@@ -690,6 +705,19 @@ contract DeployImplementations is Script { ...@@ -690,6 +705,19 @@ contract DeployImplementations is Script {
_dio.set(_dio.disputeGameFactoryImpl.selector, address(impl)); _dio.set(_dio.disputeGameFactoryImpl.selector, address(impl));
} }
function deployAnchorStateRegistryImpl(DeployImplementationsOutput _dio) public virtual {
vm.broadcast(msg.sender);
IAnchorStateRegistry impl = IAnchorStateRegistry(
DeployUtils.createDeterministic({
_name: "AnchorStateRegistry",
_args: DeployUtils.encodeConstructor(abi.encodeCall(IAnchorStateRegistry.__constructor__, ())),
_salt: _salt
})
);
vm.label(address(impl), "AnchorStateRegistryImpl");
_dio.set(_dio.anchorStateRegistryImpl.selector, address(impl));
}
// -------- Utilities -------- // -------- Utilities --------
function etchIOContracts() public returns (DeployImplementationsInput dii_, DeployImplementationsOutput dio_) { function etchIOContracts() public returns (DeployImplementationsInput dii_, DeployImplementationsOutput dio_) {
...@@ -769,6 +797,7 @@ contract DeployImplementationsInterop is DeployImplementations { ...@@ -769,6 +797,7 @@ contract DeployImplementationsInterop is DeployImplementations {
l1CrossDomainMessengerImpl: address(_dio.l1CrossDomainMessengerImpl()), l1CrossDomainMessengerImpl: address(_dio.l1CrossDomainMessengerImpl()),
l1StandardBridgeImpl: address(_dio.l1StandardBridgeImpl()), l1StandardBridgeImpl: address(_dio.l1StandardBridgeImpl()),
disputeGameFactoryImpl: address(_dio.disputeGameFactoryImpl()), disputeGameFactoryImpl: address(_dio.disputeGameFactoryImpl()),
anchorStateRegistryImpl: address(_dio.anchorStateRegistryImpl()),
delayedWETHImpl: address(_dio.delayedWETHImpl()), delayedWETHImpl: address(_dio.delayedWETHImpl()),
mipsImpl: address(_dio.mipsSingleton()) mipsImpl: address(_dio.mipsSingleton())
}); });
......
...@@ -22,7 +22,6 @@ contract DeployOPCMInput is BaseDeployIO { ...@@ -22,7 +22,6 @@ contract DeployOPCMInput is BaseDeployIO {
address internal _proxyAdminBlueprint; address internal _proxyAdminBlueprint;
address internal _l1ChugSplashProxyBlueprint; address internal _l1ChugSplashProxyBlueprint;
address internal _resolvedDelegateProxyBlueprint; address internal _resolvedDelegateProxyBlueprint;
address internal _anchorStateRegistryBlueprint;
address internal _permissionedDisputeGame1Blueprint; address internal _permissionedDisputeGame1Blueprint;
address internal _permissionedDisputeGame2Blueprint; address internal _permissionedDisputeGame2Blueprint;
...@@ -33,6 +32,7 @@ contract DeployOPCMInput is BaseDeployIO { ...@@ -33,6 +32,7 @@ contract DeployOPCMInput is BaseDeployIO {
address internal _l1CrossDomainMessengerImpl; address internal _l1CrossDomainMessengerImpl;
address internal _l1StandardBridgeImpl; address internal _l1StandardBridgeImpl;
address internal _disputeGameFactoryImpl; address internal _disputeGameFactoryImpl;
address internal _anchorStateRegistryImpl;
address internal _delayedWETHImpl; address internal _delayedWETHImpl;
address internal _mipsImpl; address internal _mipsImpl;
...@@ -47,7 +47,6 @@ contract DeployOPCMInput is BaseDeployIO { ...@@ -47,7 +47,6 @@ contract DeployOPCMInput is BaseDeployIO {
else if (_sel == this.proxyAdminBlueprint.selector) _proxyAdminBlueprint = _addr; else if (_sel == this.proxyAdminBlueprint.selector) _proxyAdminBlueprint = _addr;
else if (_sel == this.l1ChugSplashProxyBlueprint.selector) _l1ChugSplashProxyBlueprint = _addr; else if (_sel == this.l1ChugSplashProxyBlueprint.selector) _l1ChugSplashProxyBlueprint = _addr;
else if (_sel == this.resolvedDelegateProxyBlueprint.selector) _resolvedDelegateProxyBlueprint = _addr; else if (_sel == this.resolvedDelegateProxyBlueprint.selector) _resolvedDelegateProxyBlueprint = _addr;
else if (_sel == this.anchorStateRegistryBlueprint.selector) _anchorStateRegistryBlueprint = _addr;
else if (_sel == this.permissionedDisputeGame1Blueprint.selector) _permissionedDisputeGame1Blueprint = _addr; else if (_sel == this.permissionedDisputeGame1Blueprint.selector) _permissionedDisputeGame1Blueprint = _addr;
else if (_sel == this.permissionedDisputeGame2Blueprint.selector) _permissionedDisputeGame2Blueprint = _addr; else if (_sel == this.permissionedDisputeGame2Blueprint.selector) _permissionedDisputeGame2Blueprint = _addr;
else if (_sel == this.l1ERC721BridgeImpl.selector) _l1ERC721BridgeImpl = _addr; else if (_sel == this.l1ERC721BridgeImpl.selector) _l1ERC721BridgeImpl = _addr;
...@@ -57,6 +56,7 @@ contract DeployOPCMInput is BaseDeployIO { ...@@ -57,6 +56,7 @@ contract DeployOPCMInput is BaseDeployIO {
else if (_sel == this.l1CrossDomainMessengerImpl.selector) _l1CrossDomainMessengerImpl = _addr; else if (_sel == this.l1CrossDomainMessengerImpl.selector) _l1CrossDomainMessengerImpl = _addr;
else if (_sel == this.l1StandardBridgeImpl.selector) _l1StandardBridgeImpl = _addr; else if (_sel == this.l1StandardBridgeImpl.selector) _l1StandardBridgeImpl = _addr;
else if (_sel == this.disputeGameFactoryImpl.selector) _disputeGameFactoryImpl = _addr; else if (_sel == this.disputeGameFactoryImpl.selector) _disputeGameFactoryImpl = _addr;
else if (_sel == this.anchorStateRegistryImpl.selector) _anchorStateRegistryImpl = _addr;
else if (_sel == this.delayedWETHImpl.selector) _delayedWETHImpl = _addr; else if (_sel == this.delayedWETHImpl.selector) _delayedWETHImpl = _addr;
else if (_sel == this.mipsImpl.selector) _mipsImpl = _addr; else if (_sel == this.mipsImpl.selector) _mipsImpl = _addr;
else revert("DeployOPCMInput: unknown selector"); else revert("DeployOPCMInput: unknown selector");
...@@ -110,11 +110,6 @@ contract DeployOPCMInput is BaseDeployIO { ...@@ -110,11 +110,6 @@ contract DeployOPCMInput is BaseDeployIO {
return _resolvedDelegateProxyBlueprint; return _resolvedDelegateProxyBlueprint;
} }
function anchorStateRegistryBlueprint() public view returns (address) {
require(_anchorStateRegistryBlueprint != address(0), "DeployOPCMInput: not set");
return _anchorStateRegistryBlueprint;
}
function permissionedDisputeGame1Blueprint() public view returns (address) { function permissionedDisputeGame1Blueprint() public view returns (address) {
require(_permissionedDisputeGame1Blueprint != address(0), "DeployOPCMInput: not set"); require(_permissionedDisputeGame1Blueprint != address(0), "DeployOPCMInput: not set");
return _permissionedDisputeGame1Blueprint; return _permissionedDisputeGame1Blueprint;
...@@ -160,6 +155,11 @@ contract DeployOPCMInput is BaseDeployIO { ...@@ -160,6 +155,11 @@ contract DeployOPCMInput is BaseDeployIO {
return _disputeGameFactoryImpl; return _disputeGameFactoryImpl;
} }
function anchorStateRegistryImpl() public view returns (address) {
require(_anchorStateRegistryImpl != address(0), "DeployOPCMInput: not set");
return _anchorStateRegistryImpl;
}
function delayedWETHImpl() public view returns (address) { function delayedWETHImpl() public view returns (address) {
require(_delayedWETHImpl != address(0), "DeployOPCMInput: not set"); require(_delayedWETHImpl != address(0), "DeployOPCMInput: not set");
return _delayedWETHImpl; return _delayedWETHImpl;
...@@ -196,7 +196,6 @@ contract DeployOPCM is Script { ...@@ -196,7 +196,6 @@ contract DeployOPCM is Script {
proxyAdmin: _doi.proxyAdminBlueprint(), proxyAdmin: _doi.proxyAdminBlueprint(),
l1ChugSplashProxy: _doi.l1ChugSplashProxyBlueprint(), l1ChugSplashProxy: _doi.l1ChugSplashProxyBlueprint(),
resolvedDelegateProxy: _doi.resolvedDelegateProxyBlueprint(), resolvedDelegateProxy: _doi.resolvedDelegateProxyBlueprint(),
anchorStateRegistry: _doi.anchorStateRegistryBlueprint(),
permissionedDisputeGame1: _doi.permissionedDisputeGame1Blueprint(), permissionedDisputeGame1: _doi.permissionedDisputeGame1Blueprint(),
permissionedDisputeGame2: _doi.permissionedDisputeGame2Blueprint(), permissionedDisputeGame2: _doi.permissionedDisputeGame2Blueprint(),
permissionlessDisputeGame1: address(0), permissionlessDisputeGame1: address(0),
...@@ -210,6 +209,7 @@ contract DeployOPCM is Script { ...@@ -210,6 +209,7 @@ contract DeployOPCM is Script {
l1CrossDomainMessengerImpl: address(_doi.l1CrossDomainMessengerImpl()), l1CrossDomainMessengerImpl: address(_doi.l1CrossDomainMessengerImpl()),
l1StandardBridgeImpl: address(_doi.l1StandardBridgeImpl()), l1StandardBridgeImpl: address(_doi.l1StandardBridgeImpl()),
disputeGameFactoryImpl: address(_doi.disputeGameFactoryImpl()), disputeGameFactoryImpl: address(_doi.disputeGameFactoryImpl()),
anchorStateRegistryImpl: address(_doi.anchorStateRegistryImpl()),
delayedWETHImpl: address(_doi.delayedWETHImpl()), delayedWETHImpl: address(_doi.delayedWETHImpl()),
mipsImpl: address(_doi.mipsImpl()) mipsImpl: address(_doi.mipsImpl())
}); });
...@@ -251,7 +251,6 @@ contract DeployOPCM is Script { ...@@ -251,7 +251,6 @@ contract DeployOPCM is Script {
require(blueprints.proxyAdmin == _doi.proxyAdminBlueprint(), "OPCMI-60"); require(blueprints.proxyAdmin == _doi.proxyAdminBlueprint(), "OPCMI-60");
require(blueprints.l1ChugSplashProxy == _doi.l1ChugSplashProxyBlueprint(), "OPCMI-70"); require(blueprints.l1ChugSplashProxy == _doi.l1ChugSplashProxyBlueprint(), "OPCMI-70");
require(blueprints.resolvedDelegateProxy == _doi.resolvedDelegateProxyBlueprint(), "OPCMI-80"); require(blueprints.resolvedDelegateProxy == _doi.resolvedDelegateProxyBlueprint(), "OPCMI-80");
require(blueprints.anchorStateRegistry == _doi.anchorStateRegistryBlueprint(), "OPCMI-90");
require(blueprints.permissionedDisputeGame1 == _doi.permissionedDisputeGame1Blueprint(), "OPCMI-100"); require(blueprints.permissionedDisputeGame1 == _doi.permissionedDisputeGame1Blueprint(), "OPCMI-100");
require(blueprints.permissionedDisputeGame2 == _doi.permissionedDisputeGame2Blueprint(), "OPCMI-110"); require(blueprints.permissionedDisputeGame2 == _doi.permissionedDisputeGame2Blueprint(), "OPCMI-110");
...@@ -265,8 +264,9 @@ contract DeployOPCM is Script { ...@@ -265,8 +264,9 @@ contract DeployOPCM is Script {
require(implementations.l1CrossDomainMessengerImpl == _doi.l1CrossDomainMessengerImpl(), "OPCMI-160"); require(implementations.l1CrossDomainMessengerImpl == _doi.l1CrossDomainMessengerImpl(), "OPCMI-160");
require(implementations.l1StandardBridgeImpl == _doi.l1StandardBridgeImpl(), "OPCMI-170"); require(implementations.l1StandardBridgeImpl == _doi.l1StandardBridgeImpl(), "OPCMI-170");
require(implementations.disputeGameFactoryImpl == _doi.disputeGameFactoryImpl(), "OPCMI-180"); require(implementations.disputeGameFactoryImpl == _doi.disputeGameFactoryImpl(), "OPCMI-180");
require(implementations.delayedWETHImpl == _doi.delayedWETHImpl(), "OPCMI-190"); require(implementations.anchorStateRegistryImpl == _doi.anchorStateRegistryImpl(), "OPCMI-190");
require(implementations.mipsImpl == _doi.mipsImpl(), "OPCMI-200"); require(implementations.delayedWETHImpl == _doi.delayedWETHImpl(), "OPCMI-200");
require(implementations.mipsImpl == _doi.mipsImpl(), "OPCMI-210");
} }
function etchIOContracts() public returns (DeployOPCMInput doi_, DeployOPCMOutput doo_) { function etchIOContracts() public returns (DeployOPCMInput doi_, DeployOPCMOutput doo_) {
......
...@@ -159,7 +159,7 @@ contract DeployOPChainInput is BaseDeployIO { ...@@ -159,7 +159,7 @@ contract DeployOPChainInput is BaseDeployIO {
return _l2ChainId; return _l2ChainId;
} }
function startingAnchorRoots() public pure returns (bytes memory) { function startingAnchorRoot() public pure returns (bytes memory) {
// WARNING: For now always hardcode the starting permissioned game anchor root to 0xdead, // WARNING: For now always hardcode the starting permissioned game anchor root to 0xdead,
// and we do not set anything for the permissioned game. This is because we currently only // and we do not set anything for the permissioned game. This is because we currently only
// support deploying straight to permissioned games, and the starting root does not // support deploying straight to permissioned games, and the starting root does not
...@@ -168,10 +168,10 @@ contract DeployOPChainInput is BaseDeployIO { ...@@ -168,10 +168,10 @@ contract DeployOPChainInput is BaseDeployIO {
// because to to update to the permissionless game, we will need to update its starting // because to to update to the permissionless game, we will need to update its starting
// anchor root and deploy a new permissioned dispute game contract anyway. // anchor root and deploy a new permissioned dispute game contract anyway.
// //
// You can `console.logBytes(abi.encode(ScriptConstants.DEFAULT_STARTING_ANCHOR_ROOTS()))` to get the bytes that // You can `console.logBytes(abi.encode(ScriptConstants.DEFAULT_OUTPUT_ROOT()))` to get the bytes that
// are hardcoded into `op-chain-ops/deployer/opcm/opchain.go` // are hardcoded into `op-chain-ops/deployer/opcm/opchain.go`
return abi.encode(ScriptConstants.DEFAULT_STARTING_ANCHOR_ROOTS()); return abi.encode(ScriptConstants.DEFAULT_OUTPUT_ROOT());
} }
function opcm() public view returns (OPContractsManager) { function opcm() public view returns (OPContractsManager) {
...@@ -228,7 +228,6 @@ contract DeployOPChainOutput is BaseDeployIO { ...@@ -228,7 +228,6 @@ contract DeployOPChainOutput is BaseDeployIO {
IOptimismPortal2 internal _optimismPortalProxy; IOptimismPortal2 internal _optimismPortalProxy;
IDisputeGameFactory internal _disputeGameFactoryProxy; IDisputeGameFactory internal _disputeGameFactoryProxy;
IAnchorStateRegistry internal _anchorStateRegistryProxy; IAnchorStateRegistry internal _anchorStateRegistryProxy;
IAnchorStateRegistry internal _anchorStateRegistryImpl;
IFaultDisputeGame internal _faultDisputeGame; IFaultDisputeGame internal _faultDisputeGame;
IPermissionedDisputeGame internal _permissionedDisputeGame; IPermissionedDisputeGame internal _permissionedDisputeGame;
IDelayedWETH internal _delayedWETHPermissionedGameProxy; IDelayedWETH internal _delayedWETHPermissionedGameProxy;
...@@ -247,7 +246,6 @@ contract DeployOPChainOutput is BaseDeployIO { ...@@ -247,7 +246,6 @@ contract DeployOPChainOutput is BaseDeployIO {
else if (_sel == this.optimismPortalProxy.selector) _optimismPortalProxy = IOptimismPortal2(payable(_addr)) ; else if (_sel == this.optimismPortalProxy.selector) _optimismPortalProxy = IOptimismPortal2(payable(_addr)) ;
else if (_sel == this.disputeGameFactoryProxy.selector) _disputeGameFactoryProxy = IDisputeGameFactory(_addr) ; else if (_sel == this.disputeGameFactoryProxy.selector) _disputeGameFactoryProxy = IDisputeGameFactory(_addr) ;
else if (_sel == this.anchorStateRegistryProxy.selector) _anchorStateRegistryProxy = IAnchorStateRegistry(_addr) ; else if (_sel == this.anchorStateRegistryProxy.selector) _anchorStateRegistryProxy = IAnchorStateRegistry(_addr) ;
else if (_sel == this.anchorStateRegistryImpl.selector) _anchorStateRegistryImpl = IAnchorStateRegistry(_addr) ;
else if (_sel == this.faultDisputeGame.selector) _faultDisputeGame = IFaultDisputeGame(_addr) ; else if (_sel == this.faultDisputeGame.selector) _faultDisputeGame = IFaultDisputeGame(_addr) ;
else if (_sel == this.permissionedDisputeGame.selector) _permissionedDisputeGame = IPermissionedDisputeGame(_addr) ; else if (_sel == this.permissionedDisputeGame.selector) _permissionedDisputeGame = IPermissionedDisputeGame(_addr) ;
else if (_sel == this.delayedWETHPermissionedGameProxy.selector) _delayedWETHPermissionedGameProxy = IDelayedWETH(payable(_addr)) ; else if (_sel == this.delayedWETHPermissionedGameProxy.selector) _delayedWETHPermissionedGameProxy = IDelayedWETH(payable(_addr)) ;
...@@ -314,11 +312,6 @@ contract DeployOPChainOutput is BaseDeployIO { ...@@ -314,11 +312,6 @@ contract DeployOPChainOutput is BaseDeployIO {
return _anchorStateRegistryProxy; return _anchorStateRegistryProxy;
} }
function anchorStateRegistryImpl() public view returns (IAnchorStateRegistry) {
DeployUtils.assertValidContractAddress(address(_anchorStateRegistryImpl));
return _anchorStateRegistryImpl;
}
function faultDisputeGame() public view returns (IFaultDisputeGame) { function faultDisputeGame() public view returns (IFaultDisputeGame) {
DeployUtils.assertValidContractAddress(address(_faultDisputeGame)); DeployUtils.assertValidContractAddress(address(_faultDisputeGame));
return _faultDisputeGame; return _faultDisputeGame;
...@@ -361,7 +354,7 @@ contract DeployOPChain is Script { ...@@ -361,7 +354,7 @@ contract DeployOPChain is Script {
basefeeScalar: _doi.basefeeScalar(), basefeeScalar: _doi.basefeeScalar(),
blobBasefeeScalar: _doi.blobBaseFeeScalar(), blobBasefeeScalar: _doi.blobBaseFeeScalar(),
l2ChainId: _doi.l2ChainId(), l2ChainId: _doi.l2ChainId(),
startingAnchorRoots: _doi.startingAnchorRoots(), startingAnchorRoot: _doi.startingAnchorRoot(),
saltMixer: _doi.saltMixer(), saltMixer: _doi.saltMixer(),
gasLimit: _doi.gasLimit(), gasLimit: _doi.gasLimit(),
disputeGameType: _doi.disputeGameType(), disputeGameType: _doi.disputeGameType(),
...@@ -385,7 +378,6 @@ contract DeployOPChain is Script { ...@@ -385,7 +378,6 @@ contract DeployOPChain is Script {
vm.label(address(deployOutput.optimismPortalProxy), "optimismPortalProxy"); vm.label(address(deployOutput.optimismPortalProxy), "optimismPortalProxy");
vm.label(address(deployOutput.disputeGameFactoryProxy), "disputeGameFactoryProxy"); vm.label(address(deployOutput.disputeGameFactoryProxy), "disputeGameFactoryProxy");
vm.label(address(deployOutput.anchorStateRegistryProxy), "anchorStateRegistryProxy"); vm.label(address(deployOutput.anchorStateRegistryProxy), "anchorStateRegistryProxy");
vm.label(address(deployOutput.anchorStateRegistryImpl), "anchorStateRegistryImpl");
// vm.label(address(deployOutput.faultDisputeGame), "faultDisputeGame"); // vm.label(address(deployOutput.faultDisputeGame), "faultDisputeGame");
vm.label(address(deployOutput.permissionedDisputeGame), "permissionedDisputeGame"); vm.label(address(deployOutput.permissionedDisputeGame), "permissionedDisputeGame");
vm.label(address(deployOutput.delayedWETHPermissionedGameProxy), "delayedWETHPermissionedGameProxy"); vm.label(address(deployOutput.delayedWETHPermissionedGameProxy), "delayedWETHPermissionedGameProxy");
...@@ -404,7 +396,6 @@ contract DeployOPChain is Script { ...@@ -404,7 +396,6 @@ contract DeployOPChain is Script {
_doo.set(_doo.optimismPortalProxy.selector, address(deployOutput.optimismPortalProxy)); _doo.set(_doo.optimismPortalProxy.selector, address(deployOutput.optimismPortalProxy));
_doo.set(_doo.disputeGameFactoryProxy.selector, address(deployOutput.disputeGameFactoryProxy)); _doo.set(_doo.disputeGameFactoryProxy.selector, address(deployOutput.disputeGameFactoryProxy));
_doo.set(_doo.anchorStateRegistryProxy.selector, address(deployOutput.anchorStateRegistryProxy)); _doo.set(_doo.anchorStateRegistryProxy.selector, address(deployOutput.anchorStateRegistryProxy));
_doo.set(_doo.anchorStateRegistryImpl.selector, address(deployOutput.anchorStateRegistryImpl));
// _doo.set(_doo.faultDisputeGame.selector, address(deployOutput.faultDisputeGame)); // _doo.set(_doo.faultDisputeGame.selector, address(deployOutput.faultDisputeGame));
_doo.set(_doo.permissionedDisputeGame.selector, address(deployOutput.permissionedDisputeGame)); _doo.set(_doo.permissionedDisputeGame.selector, address(deployOutput.permissionedDisputeGame));
_doo.set(_doo.delayedWETHPermissionedGameProxy.selector, address(deployOutput.delayedWETHPermissionedGameProxy)); _doo.set(_doo.delayedWETHPermissionedGameProxy.selector, address(deployOutput.delayedWETHPermissionedGameProxy));
...@@ -433,7 +424,6 @@ contract DeployOPChain is Script { ...@@ -433,7 +424,6 @@ contract DeployOPChain is Script {
address(_doo.optimismPortalProxy()), address(_doo.optimismPortalProxy()),
address(_doo.disputeGameFactoryProxy()), address(_doo.disputeGameFactoryProxy()),
address(_doo.anchorStateRegistryProxy()), address(_doo.anchorStateRegistryProxy()),
address(_doo.anchorStateRegistryImpl()),
address(_doo.permissionedDisputeGame()), address(_doo.permissionedDisputeGame()),
// address(_doo.faultDisputeGame()), // address(_doo.faultDisputeGame()),
address(_doo.delayedWETHPermissionedGameProxy()) address(_doo.delayedWETHPermissionedGameProxy())
...@@ -447,7 +437,6 @@ contract DeployOPChain is Script { ...@@ -447,7 +437,6 @@ contract DeployOPChain is Script {
// -------- Deployment Assertions -------- // -------- Deployment Assertions --------
function assertValidDeploy(DeployOPChainInput _doi, DeployOPChainOutput _doo) internal { function assertValidDeploy(DeployOPChainInput _doi, DeployOPChainOutput _doo) internal {
assertValidAnchorStateRegistryImpl(_doi, _doo);
assertValidAnchorStateRegistryProxy(_doi, _doo); assertValidAnchorStateRegistryProxy(_doi, _doo);
assertValidDelayedWETH(_doi, _doo); assertValidDelayedWETH(_doi, _doo);
assertValidDisputeGameFactory(_doi, _doo); assertValidDisputeGameFactory(_doi, _doo);
...@@ -508,9 +497,6 @@ contract DeployOPChain is Script { ...@@ -508,9 +497,6 @@ contract DeployOPChain is Script {
_offset: 0 _offset: 0
}); });
vm.prank(address(0));
address impl = proxy.implementation();
require(impl == address(_doo.anchorStateRegistryImpl()), "ANCHORP-20");
require( require(
address(_doo.anchorStateRegistryProxy().disputeGameFactory()) == address(_doo.disputeGameFactoryProxy()), address(_doo.anchorStateRegistryProxy().disputeGameFactory()) == address(_doo.disputeGameFactoryProxy()),
"ANCHORP-30" "ANCHORP-30"
...@@ -521,14 +507,6 @@ contract DeployOPChain is Script { ...@@ -521,14 +507,6 @@ contract DeployOPChain is Script {
require(Hash.unwrap(actualRoot) == expectedRoot, "ANCHORP-40"); require(Hash.unwrap(actualRoot) == expectedRoot, "ANCHORP-40");
} }
function assertValidAnchorStateRegistryImpl(DeployOPChainInput, DeployOPChainOutput _doo) internal {
IAnchorStateRegistry registry = _doo.anchorStateRegistryImpl();
DeployUtils.assertInitialized({ _contractAddress: address(registry), _isProxy: false, _slot: 0, _offset: 0 });
require(address(registry.disputeGameFactory()) == address(_doo.disputeGameFactoryProxy()), "ANCHORI-10");
}
function assertValidSystemConfig(DeployOPChainInput _doi, DeployOPChainOutput _doo) internal { function assertValidSystemConfig(DeployOPChainInput _doi, DeployOPChainOutput _doo) internal {
ISystemConfig systemConfig = _doo.systemConfigProxy(); ISystemConfig systemConfig = _doo.systemConfigProxy();
......
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity ^0.8.0; pragma solidity ^0.8.0;
import { IAnchorStateRegistry } from "interfaces/dispute/IAnchorStateRegistry.sol"; import { OutputRoot, Hash } from "src/dispute/lib/Types.sol";
import { GameTypes, OutputRoot, Hash } from "src/dispute/lib/Types.sol";
/// @title Constants /// @title Constants
/// @notice Constants is a library for storing constants. Simple! Don't put everything in here, just /// @notice Constants is a library for storing constants. Simple! Don't put everything in here, just
...@@ -13,14 +12,4 @@ library Constants { ...@@ -13,14 +12,4 @@ library Constants {
function DEFAULT_OUTPUT_ROOT() internal pure returns (OutputRoot memory) { function DEFAULT_OUTPUT_ROOT() internal pure returns (OutputRoot memory) {
return OutputRoot({ root: Hash.wrap(bytes32(hex"dead")), l2BlockNumber: 0 }); return OutputRoot({ root: Hash.wrap(bytes32(hex"dead")), l2BlockNumber: 0 });
} }
function DEFAULT_STARTING_ANCHOR_ROOTS() internal pure returns (IAnchorStateRegistry.StartingAnchorRoot[] memory) {
IAnchorStateRegistry.StartingAnchorRoot[] memory defaultStartingAnchorRoots =
new IAnchorStateRegistry.StartingAnchorRoot[](1);
defaultStartingAnchorRoots[0] = IAnchorStateRegistry.StartingAnchorRoot({
gameType: GameTypes.PERMISSIONED_CANNON,
outputRoot: DEFAULT_OUTPUT_ROOT()
});
return defaultStartingAnchorRoots;
}
} }
[ [
{ {
"inputs": [ "inputs": [],
"stateMutability": "nonpayable",
"type": "constructor"
},
{ {
"internalType": "contract IDisputeGameFactory", "inputs": [],
"name": "_disputeGameFactory", "name": "anchorGame",
"outputs": [
{
"internalType": "contract IFaultDisputeGame",
"name": "",
"type": "address" "type": "address"
} }
], ],
"stateMutability": "nonpayable", "stateMutability": "view",
"type": "constructor" "type": "function"
}, },
{ {
"inputs": [ "inputs": [
...@@ -22,12 +29,12 @@ ...@@ -22,12 +29,12 @@
"outputs": [ "outputs": [
{ {
"internalType": "Hash", "internalType": "Hash",
"name": "root", "name": "",
"type": "bytes32" "type": "bytes32"
}, },
{ {
"internalType": "uint256", "internalType": "uint256",
"name": "l2BlockNumber", "name": "",
"type": "uint256" "type": "uint256"
} }
], ],
...@@ -47,14 +54,40 @@ ...@@ -47,14 +54,40 @@
"stateMutability": "view", "stateMutability": "view",
"type": "function" "type": "function"
}, },
{
"inputs": [],
"name": "getAnchorRoot",
"outputs": [
{
"internalType": "Hash",
"name": "",
"type": "bytes32"
},
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{ {
"inputs": [ "inputs": [
{ {
"components": [ "internalType": "contract ISuperchainConfig",
"name": "_superchainConfig",
"type": "address"
},
{ {
"internalType": "GameType", "internalType": "contract IDisputeGameFactory",
"name": "gameType", "name": "_disputeGameFactory",
"type": "uint32" "type": "address"
},
{
"internalType": "contract IOptimismPortal2",
"name": "_portal",
"type": "address"
}, },
{ {
"components": [ "components": [
...@@ -70,23 +103,121 @@ ...@@ -70,23 +103,121 @@
} }
], ],
"internalType": "struct OutputRoot", "internalType": "struct OutputRoot",
"name": "outputRoot", "name": "_startingAnchorRoot",
"type": "tuple" "type": "tuple"
} }
], ],
"internalType": "struct AnchorStateRegistry.StartingAnchorRoot[]", "name": "initialize",
"name": "_startingAnchorRoots", "outputs": [],
"type": "tuple[]" "stateMutability": "nonpayable",
"type": "function"
}, },
{ {
"internalType": "contract ISuperchainConfig", "inputs": [
"name": "_superchainConfig", {
"internalType": "contract IDisputeGame",
"name": "_game",
"type": "address" "type": "address"
} }
], ],
"name": "initialize", "name": "isGameBlacklisted",
"outputs": [], "outputs": [
"stateMutability": "nonpayable", {
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "contract IDisputeGame",
"name": "_game",
"type": "address"
}
],
"name": "isGameProper",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "contract IDisputeGame",
"name": "_game",
"type": "address"
}
],
"name": "isGameRegistered",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "contract IDisputeGame",
"name": "_game",
"type": "address"
}
],
"name": "isGameRespected",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "contract IDisputeGame",
"name": "_game",
"type": "address"
}
],
"name": "isGameRetired",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "portal",
"outputs": [
{
"internalType": "contract IOptimismPortal2",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function" "type": "function"
}, },
{ {
...@@ -135,6 +266,32 @@ ...@@ -135,6 +266,32 @@
"stateMutability": "view", "stateMutability": "view",
"type": "function" "type": "function"
}, },
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "contract IFaultDisputeGame",
"name": "game",
"type": "address"
}
],
"name": "AnchorNotUpdated",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "contract IFaultDisputeGame",
"name": "game",
"type": "address"
}
],
"name": "AnchorUpdated",
"type": "event"
},
{ {
"anonymous": false, "anonymous": false,
"inputs": [ "inputs": [
...@@ -150,17 +307,17 @@ ...@@ -150,17 +307,17 @@
}, },
{ {
"inputs": [], "inputs": [],
"name": "InvalidGameStatus", "name": "AnchorStateRegistry_ImproperAnchorGame",
"type": "error" "type": "error"
}, },
{ {
"inputs": [], "inputs": [],
"name": "Unauthorized", "name": "AnchorStateRegistry_InvalidAnchorGame",
"type": "error" "type": "error"
}, },
{ {
"inputs": [], "inputs": [],
"name": "UnregisteredGame", "name": "AnchorStateRegistry_Unauthorized",
"type": "error" "type": "error"
} }
] ]
\ No newline at end of file
...@@ -43,11 +43,6 @@ ...@@ -43,11 +43,6 @@
"name": "resolvedDelegateProxy", "name": "resolvedDelegateProxy",
"type": "address" "type": "address"
}, },
{
"internalType": "address",
"name": "anchorStateRegistry",
"type": "address"
},
{ {
"internalType": "address", "internalType": "address",
"name": "permissionedDisputeGame1", "name": "permissionedDisputeGame1",
...@@ -110,6 +105,11 @@ ...@@ -110,6 +105,11 @@
"name": "disputeGameFactoryImpl", "name": "disputeGameFactoryImpl",
"type": "address" "type": "address"
}, },
{
"internalType": "address",
"name": "anchorStateRegistryImpl",
"type": "address"
},
{ {
"internalType": "address", "internalType": "address",
"name": "delayedWETHImpl", "name": "delayedWETHImpl",
...@@ -271,11 +271,6 @@ ...@@ -271,11 +271,6 @@
"name": "resolvedDelegateProxy", "name": "resolvedDelegateProxy",
"type": "address" "type": "address"
}, },
{
"internalType": "address",
"name": "anchorStateRegistry",
"type": "address"
},
{ {
"internalType": "address", "internalType": "address",
"name": "permissionedDisputeGame1", "name": "permissionedDisputeGame1",
...@@ -382,7 +377,7 @@ ...@@ -382,7 +377,7 @@
}, },
{ {
"internalType": "bytes", "internalType": "bytes",
"name": "startingAnchorRoots", "name": "startingAnchorRoot",
"type": "bytes" "type": "bytes"
}, },
{ {
...@@ -485,11 +480,6 @@ ...@@ -485,11 +480,6 @@
"name": "anchorStateRegistryProxy", "name": "anchorStateRegistryProxy",
"type": "address" "type": "address"
}, },
{
"internalType": "contract IAnchorStateRegistry",
"name": "anchorStateRegistryImpl",
"type": "address"
},
{ {
"internalType": "contract IFaultDisputeGame", "internalType": "contract IFaultDisputeGame",
"name": "faultDisputeGame", "name": "faultDisputeGame",
...@@ -560,6 +550,11 @@ ...@@ -560,6 +550,11 @@
"name": "disputeGameFactoryImpl", "name": "disputeGameFactoryImpl",
"type": "address" "type": "address"
}, },
{
"internalType": "address",
"name": "anchorStateRegistryImpl",
"type": "address"
},
{ {
"internalType": "address", "internalType": "address",
"name": "delayedWETHImpl", "name": "delayedWETHImpl",
...@@ -732,7 +727,7 @@ ...@@ -732,7 +727,7 @@
}, },
{ {
"inputs": [], "inputs": [],
"name": "InvalidStartingAnchorRoots", "name": "InvalidStartingAnchorRoot",
"type": "error" "type": "error"
}, },
{ {
......
...@@ -43,11 +43,6 @@ ...@@ -43,11 +43,6 @@
"name": "resolvedDelegateProxy", "name": "resolvedDelegateProxy",
"type": "address" "type": "address"
}, },
{
"internalType": "address",
"name": "anchorStateRegistry",
"type": "address"
},
{ {
"internalType": "address", "internalType": "address",
"name": "permissionedDisputeGame1", "name": "permissionedDisputeGame1",
...@@ -110,6 +105,11 @@ ...@@ -110,6 +105,11 @@
"name": "disputeGameFactoryImpl", "name": "disputeGameFactoryImpl",
"type": "address" "type": "address"
}, },
{
"internalType": "address",
"name": "anchorStateRegistryImpl",
"type": "address"
},
{ {
"internalType": "address", "internalType": "address",
"name": "delayedWETHImpl", "name": "delayedWETHImpl",
...@@ -271,11 +271,6 @@ ...@@ -271,11 +271,6 @@
"name": "resolvedDelegateProxy", "name": "resolvedDelegateProxy",
"type": "address" "type": "address"
}, },
{
"internalType": "address",
"name": "anchorStateRegistry",
"type": "address"
},
{ {
"internalType": "address", "internalType": "address",
"name": "permissionedDisputeGame1", "name": "permissionedDisputeGame1",
...@@ -382,7 +377,7 @@ ...@@ -382,7 +377,7 @@
}, },
{ {
"internalType": "bytes", "internalType": "bytes",
"name": "startingAnchorRoots", "name": "startingAnchorRoot",
"type": "bytes" "type": "bytes"
}, },
{ {
...@@ -485,11 +480,6 @@ ...@@ -485,11 +480,6 @@
"name": "anchorStateRegistryProxy", "name": "anchorStateRegistryProxy",
"type": "address" "type": "address"
}, },
{
"internalType": "contract IAnchorStateRegistry",
"name": "anchorStateRegistryImpl",
"type": "address"
},
{ {
"internalType": "contract IFaultDisputeGame", "internalType": "contract IFaultDisputeGame",
"name": "faultDisputeGame", "name": "faultDisputeGame",
...@@ -560,6 +550,11 @@ ...@@ -560,6 +550,11 @@
"name": "disputeGameFactoryImpl", "name": "disputeGameFactoryImpl",
"type": "address" "type": "address"
}, },
{
"internalType": "address",
"name": "anchorStateRegistryImpl",
"type": "address"
},
{ {
"internalType": "address", "internalType": "address",
"name": "delayedWETHImpl", "name": "delayedWETHImpl",
...@@ -732,7 +727,7 @@ ...@@ -732,7 +727,7 @@
}, },
{ {
"inputs": [], "inputs": [],
"name": "InvalidStartingAnchorRoots", "name": "InvalidStartingAnchorRoot",
"type": "error" "type": "error"
}, },
{ {
......
...@@ -16,8 +16,8 @@ ...@@ -16,8 +16,8 @@
"sourceCodeHash": "0x51f0876ab8410ce32838483f8f59ad6d1c5b4a368e47415b30e44baf291a394b" "sourceCodeHash": "0x51f0876ab8410ce32838483f8f59ad6d1c5b4a368e47415b30e44baf291a394b"
}, },
"src/L1/OPContractsManager.sol": { "src/L1/OPContractsManager.sol": {
"initCodeHash": "0x8bef0b53e7102491957d3ea12ff4857d735dada6af3ef122376bcf3f5489c9b9", "initCodeHash": "0x88a6d99e668340e3af5c728c29e94e7229d89da0762b4bbf93bc10e596795c9f",
"sourceCodeHash": "0x819e5e9867e09d16346daf81cfc9c129bf9026308055d1fbb6623b4f8818e613" "sourceCodeHash": "0x2d21506cc51ebe0b60bcf89883aff5e9b1269567ce44ee779de3d3940e23fb65"
}, },
"src/L1/OptimismPortal2.sol": { "src/L1/OptimismPortal2.sol": {
"initCodeHash": "0x2121a97875875150106a54a71c6c4c03afe90b3364e416be047f55fdeab57204", "initCodeHash": "0x2121a97875875150106a54a71c6c4c03afe90b3364e416be047f55fdeab57204",
...@@ -152,8 +152,8 @@ ...@@ -152,8 +152,8 @@
"sourceCodeHash": "0xb7b0a06cd971c4647247dc19ce997d0c64a73e87c81d30731da9cf9efa1b952a" "sourceCodeHash": "0xb7b0a06cd971c4647247dc19ce997d0c64a73e87c81d30731da9cf9efa1b952a"
}, },
"src/dispute/AnchorStateRegistry.sol": { "src/dispute/AnchorStateRegistry.sol": {
"initCodeHash": "0x2d831d6afc62df024eb2df22eaca3c7378171e63d87c608bb53c0020d30c3dee", "initCodeHash": "0xfbeeac40d86d13e71c7add66eef6357576a93b6a175c9cff6ec6ef587fe3acc4",
"sourceCodeHash": "0xf1ce12bed377624e43697ae316646493e8df73a064bc4e250936964448326a70" "sourceCodeHash": "0xbb2e08da74d470fc30dd35dc39834e19f676a45974aa2403eb97e84bc5bed0a8"
}, },
"src/dispute/DelayedWETH.sol": { "src/dispute/DelayedWETH.sol": {
"initCodeHash": "0x759d7f9c52b7c13ce4502f39dae3a75d130c6278240cde0b60ae84616aa2bd48", "initCodeHash": "0x759d7f9c52b7c13ce4502f39dae3a75d130c6278240cde0b60ae84616aa2bd48",
......
...@@ -14,17 +14,38 @@ ...@@ -14,17 +14,38 @@
"type": "bool" "type": "bool"
}, },
{ {
"bytes": "32", "bytes": "20",
"label": "anchors", "label": "superchainConfig",
"offset": 2,
"slot": "0",
"type": "contract ISuperchainConfig"
},
{
"bytes": "20",
"label": "disputeGameFactory",
"offset": 0, "offset": 0,
"slot": "1", "slot": "1",
"type": "mapping(GameType => struct OutputRoot)" "type": "contract IDisputeGameFactory"
}, },
{ {
"bytes": "20", "bytes": "20",
"label": "superchainConfig", "label": "portal",
"offset": 0, "offset": 0,
"slot": "2", "slot": "2",
"type": "contract ISuperchainConfig" "type": "contract IOptimismPortal2"
},
{
"bytes": "20",
"label": "anchorGame",
"offset": 0,
"slot": "3",
"type": "contract IFaultDisputeGame"
},
{
"bytes": "64",
"label": "startingAnchorRoot",
"offset": 0,
"slot": "4",
"type": "struct OutputRoot"
} }
] ]
\ No newline at end of file
...@@ -7,17 +7,17 @@ ...@@ -7,17 +7,17 @@
"type": "string" "type": "string"
}, },
{ {
"bytes": "320", "bytes": "288",
"label": "blueprint", "label": "blueprint",
"offset": 0, "offset": 0,
"slot": "1", "slot": "1",
"type": "struct OPContractsManager.Blueprints" "type": "struct OPContractsManager.Blueprints"
}, },
{ {
"bytes": "288", "bytes": "320",
"label": "implementation", "label": "implementation",
"offset": 0, "offset": 0,
"slot": "11", "slot": "10",
"type": "struct OPContractsManager.Implementations" "type": "struct OPContractsManager.Implementations"
} }
] ]
\ No newline at end of file
...@@ -7,17 +7,17 @@ ...@@ -7,17 +7,17 @@
"type": "string" "type": "string"
}, },
{ {
"bytes": "320", "bytes": "288",
"label": "blueprint", "label": "blueprint",
"offset": 0, "offset": 0,
"slot": "1", "slot": "1",
"type": "struct OPContractsManager.Blueprints" "type": "struct OPContractsManager.Blueprints"
}, },
{ {
"bytes": "288", "bytes": "320",
"label": "implementation", "label": "implementation",
"offset": 0, "offset": 0,
"slot": "11", "slot": "10",
"type": "struct OPContractsManager.Implementations" "type": "struct OPContractsManager.Implementations"
} }
] ]
\ No newline at end of file
...@@ -5,7 +5,7 @@ pragma solidity 0.8.15; ...@@ -5,7 +5,7 @@ pragma solidity 0.8.15;
import { Blueprint } from "src/libraries/Blueprint.sol"; import { Blueprint } from "src/libraries/Blueprint.sol";
import { Constants } from "src/libraries/Constants.sol"; import { Constants } from "src/libraries/Constants.sol";
import { Bytes } from "src/libraries/Bytes.sol"; import { Bytes } from "src/libraries/Bytes.sol";
import { Claim, Duration, GameType, GameTypes } from "src/dispute/lib/Types.sol"; import { Claim, Duration, GameType, GameTypes, OutputRoot } from "src/dispute/lib/Types.sol";
// Interfaces // Interfaces
import { ISemver } from "interfaces/universal/ISemver.sol"; import { ISemver } from "interfaces/universal/ISemver.sol";
...@@ -18,7 +18,6 @@ import { IAddressManager } from "interfaces/legacy/IAddressManager.sol"; ...@@ -18,7 +18,6 @@ import { IAddressManager } from "interfaces/legacy/IAddressManager.sol";
import { IProxyAdmin } from "interfaces/universal/IProxyAdmin.sol"; import { IProxyAdmin } from "interfaces/universal/IProxyAdmin.sol";
import { IDelayedWETH } from "interfaces/dispute/IDelayedWETH.sol"; import { IDelayedWETH } from "interfaces/dispute/IDelayedWETH.sol";
import { IDisputeGameFactory } from "interfaces/dispute/IDisputeGameFactory.sol"; import { IDisputeGameFactory } from "interfaces/dispute/IDisputeGameFactory.sol";
import { IAnchorStateRegistry } from "interfaces/dispute/IAnchorStateRegistry.sol";
import { IFaultDisputeGame } from "interfaces/dispute/IFaultDisputeGame.sol"; import { IFaultDisputeGame } from "interfaces/dispute/IFaultDisputeGame.sol";
import { IPermissionedDisputeGame } from "interfaces/dispute/IPermissionedDisputeGame.sol"; import { IPermissionedDisputeGame } from "interfaces/dispute/IPermissionedDisputeGame.sol";
import { ISuperchainConfig } from "interfaces/L1/ISuperchainConfig.sol"; import { ISuperchainConfig } from "interfaces/L1/ISuperchainConfig.sol";
...@@ -49,9 +48,8 @@ contract OPContractsManager is ISemver { ...@@ -49,9 +48,8 @@ contract OPContractsManager is ISemver {
uint32 basefeeScalar; uint32 basefeeScalar;
uint32 blobBasefeeScalar; uint32 blobBasefeeScalar;
uint256 l2ChainId; uint256 l2ChainId;
// The correct type is AnchorStateRegistry.StartingAnchorRoot[] memory, // The correct type is OutputRoot memory but OP Deployer does not yet support structs.
// but OP Deployer does not yet support structs. bytes startingAnchorRoot;
bytes startingAnchorRoots;
// The salt mixer is used as part of making the resulting salt unique. // The salt mixer is used as part of making the resulting salt unique.
string saltMixer; string saltMixer;
uint64 gasLimit; uint64 gasLimit;
...@@ -77,7 +75,6 @@ contract OPContractsManager is ISemver { ...@@ -77,7 +75,6 @@ contract OPContractsManager is ISemver {
IOptimismPortal2 optimismPortalProxy; IOptimismPortal2 optimismPortalProxy;
IDisputeGameFactory disputeGameFactoryProxy; IDisputeGameFactory disputeGameFactoryProxy;
IAnchorStateRegistry anchorStateRegistryProxy; IAnchorStateRegistry anchorStateRegistryProxy;
IAnchorStateRegistry anchorStateRegistryImpl;
IFaultDisputeGame faultDisputeGame; IFaultDisputeGame faultDisputeGame;
IPermissionedDisputeGame permissionedDisputeGame; IPermissionedDisputeGame permissionedDisputeGame;
IDelayedWETH delayedWETHPermissionedGameProxy; IDelayedWETH delayedWETHPermissionedGameProxy;
...@@ -95,7 +92,6 @@ contract OPContractsManager is ISemver { ...@@ -95,7 +92,6 @@ contract OPContractsManager is ISemver {
address proxyAdmin; address proxyAdmin;
address l1ChugSplashProxy; address l1ChugSplashProxy;
address resolvedDelegateProxy; address resolvedDelegateProxy;
address anchorStateRegistry;
address permissionedDisputeGame1; address permissionedDisputeGame1;
address permissionedDisputeGame2; address permissionedDisputeGame2;
address permissionlessDisputeGame1; address permissionlessDisputeGame1;
...@@ -111,6 +107,7 @@ contract OPContractsManager is ISemver { ...@@ -111,6 +107,7 @@ contract OPContractsManager is ISemver {
address l1CrossDomainMessengerImpl; address l1CrossDomainMessengerImpl;
address l1StandardBridgeImpl; address l1StandardBridgeImpl;
address disputeGameFactoryImpl; address disputeGameFactoryImpl;
address anchorStateRegistryImpl;
address delayedWETHImpl; address delayedWETHImpl;
address mipsImpl; address mipsImpl;
} }
...@@ -138,8 +135,8 @@ contract OPContractsManager is ISemver { ...@@ -138,8 +135,8 @@ contract OPContractsManager is ISemver {
// -------- Constants and Variables -------- // -------- Constants and Variables --------
/// @custom:semver 1.0.0-beta.29 /// @custom:semver 1.0.0-beta.30
string public constant version = "1.0.0-beta.29"; string public constant version = "1.0.0-beta.30";
/// @notice Represents the interface version so consumers know how to decode the DeployOutput struct /// @notice Represents the interface version so consumers know how to decode the DeployOutput struct
/// that's emitted in the `Deployed` event. Whenever that struct changes, a new version should be used. /// that's emitted in the `Deployed` event. Whenever that struct changes, a new version should be used.
...@@ -198,8 +195,8 @@ contract OPContractsManager is ISemver { ...@@ -198,8 +195,8 @@ contract OPContractsManager is ISemver {
/// @notice Thrown when the latest release is not set upon initialization. /// @notice Thrown when the latest release is not set upon initialization.
error LatestReleaseNotSet(); error LatestReleaseNotSet();
/// @notice Thrown when the starting anchor roots are not provided. /// @notice Thrown when the starting anchor root is not provided.
error InvalidStartingAnchorRoots(); error InvalidStartingAnchorRoot();
/// @notice Thrown when certain methods are called outside of a DELEGATECALL. /// @notice Thrown when certain methods are called outside of a DELEGATECALL.
error OnlyDelegatecall(); error OnlyDelegatecall();
...@@ -293,15 +290,6 @@ contract OPContractsManager is ISemver { ...@@ -293,15 +290,6 @@ contract OPContractsManager is ISemver {
output.opChainProxyAdmin.setImplementationName(address(output.l1CrossDomainMessengerProxy), contractName); output.opChainProxyAdmin.setImplementationName(address(output.l1CrossDomainMessengerProxy), contractName);
// Now that all proxies are deployed, we can transfer ownership of the AddressManager to the ProxyAdmin. // Now that all proxies are deployed, we can transfer ownership of the AddressManager to the ProxyAdmin.
output.addressManager.transferOwnership(address(output.opChainProxyAdmin)); output.addressManager.transferOwnership(address(output.opChainProxyAdmin));
// The AnchorStateRegistry Implementation is not MCP Ready, and therefore requires an implementation per chain.
// It must be deployed after the DisputeGameFactoryProxy so that it can be provided as a constructor argument.
output.anchorStateRegistryImpl = IAnchorStateRegistry(
Blueprint.deployFrom(
blueprint.anchorStateRegistry,
computeSalt(l2ChainId, saltMixer, "AnchorStateRegistry"),
abi.encode(output.disputeGameFactoryProxy)
)
);
// Eventually we will switch from DelayedWETHPermissionedGameProxy to DelayedWETHPermissionlessGameProxy. // Eventually we will switch from DelayedWETHPermissionedGameProxy to DelayedWETHPermissionlessGameProxy.
output.delayedWETHPermissionedGameProxy = IDelayedWETH( output.delayedWETHPermissionedGameProxy = IDelayedWETH(
...@@ -397,11 +385,11 @@ contract OPContractsManager is ISemver { ...@@ -397,11 +385,11 @@ contract OPContractsManager is ISemver {
); );
output.disputeGameFactoryProxy.transferOwnership(address(_input.roles.opChainProxyAdminOwner)); output.disputeGameFactoryProxy.transferOwnership(address(_input.roles.opChainProxyAdminOwner));
data = encodeAnchorStateRegistryInitializer(_input); data = encodeAnchorStateRegistryInitializer(_input, output);
upgradeAndCall( upgradeAndCall(
output.opChainProxyAdmin, output.opChainProxyAdmin,
address(output.anchorStateRegistryProxy), address(output.anchorStateRegistryProxy),
address(output.anchorStateRegistryImpl), implementation.anchorStateRegistryImpl,
data data
); );
...@@ -539,7 +527,7 @@ contract OPContractsManager is ISemver { ...@@ -539,7 +527,7 @@ contract OPContractsManager is ISemver {
if (_input.roles.proposer == address(0)) revert InvalidRoleAddress("proposer"); if (_input.roles.proposer == address(0)) revert InvalidRoleAddress("proposer");
if (_input.roles.challenger == address(0)) revert InvalidRoleAddress("challenger"); if (_input.roles.challenger == address(0)) revert InvalidRoleAddress("challenger");
if (_input.startingAnchorRoots.length == 0) revert InvalidStartingAnchorRoots(); if (_input.startingAnchorRoot.length == 0) revert InvalidStartingAnchorRoot();
} }
/// @notice Maps an L2 chain ID to an L1 batch inbox address as defined by the standard /// @notice Maps an L2 chain ID to an L1 batch inbox address as defined by the standard
...@@ -686,15 +674,20 @@ contract OPContractsManager is ISemver { ...@@ -686,15 +674,20 @@ contract OPContractsManager is ISemver {
return abi.encodeCall(IDisputeGameFactory.initialize, (address(this))); return abi.encodeCall(IDisputeGameFactory.initialize, (address(this)));
} }
function encodeAnchorStateRegistryInitializer(DeployInput memory _input) function encodeAnchorStateRegistryInitializer(
DeployInput memory _input,
DeployOutput memory _output
)
internal internal
view view
virtual virtual
returns (bytes memory) returns (bytes memory)
{ {
IAnchorStateRegistry.StartingAnchorRoot[] memory startingAnchorRoots = OutputRoot memory startingAnchorRoot = abi.decode(_input.startingAnchorRoot, (OutputRoot));
abi.decode(_input.startingAnchorRoots, (IAnchorStateRegistry.StartingAnchorRoot[])); return abi.encodeCall(
return abi.encodeCall(IAnchorStateRegistry.initialize, (startingAnchorRoots, superchainConfig)); IAnchorStateRegistry.initialize,
(superchainConfig, _output.disputeGameFactoryProxy, _output.optimismPortalProxy, startingAnchorRoot)
);
} }
function encodeDelayedWETHInitializer(DeployInput memory _input) internal view virtual returns (bytes memory) { function encodeDelayedWETHInitializer(DeployInput memory _input) internal view virtual returns (bytes memory) {
......
...@@ -6,8 +6,6 @@ import { Initializable } from "@openzeppelin/contracts/proxy/utils/Initializable ...@@ -6,8 +6,6 @@ import { Initializable } from "@openzeppelin/contracts/proxy/utils/Initializable
// Libraries // Libraries
import { GameType, OutputRoot, Claim, GameStatus, Hash } from "src/dispute/lib/Types.sol"; import { GameType, OutputRoot, Claim, GameStatus, Hash } from "src/dispute/lib/Types.sol";
import { Unauthorized } from "src/libraries/errors/CommonErrors.sol";
import { UnregisteredGame, InvalidGameStatus } from "src/dispute/lib/Errors.sol";
// Interfaces // Interfaces
import { ISemver } from "interfaces/universal/ISemver.sol"; import { ISemver } from "interfaces/universal/ISemver.sol";
...@@ -15,6 +13,7 @@ import { IFaultDisputeGame } from "interfaces/dispute/IFaultDisputeGame.sol"; ...@@ -15,6 +13,7 @@ import { IFaultDisputeGame } from "interfaces/dispute/IFaultDisputeGame.sol";
import { IDisputeGame } from "interfaces/dispute/IDisputeGame.sol"; import { IDisputeGame } from "interfaces/dispute/IDisputeGame.sol";
import { IDisputeGameFactory } from "interfaces/dispute/IDisputeGameFactory.sol"; import { IDisputeGameFactory } from "interfaces/dispute/IDisputeGameFactory.sol";
import { ISuperchainConfig } from "interfaces/L1/ISuperchainConfig.sol"; import { ISuperchainConfig } from "interfaces/L1/ISuperchainConfig.sol";
import { IOptimismPortal2 } from "interfaces/L1/IOptimismPortal2.sol";
/// @custom:proxied true /// @custom:proxied true
/// @title AnchorStateRegistry /// @title AnchorStateRegistry
...@@ -23,105 +22,218 @@ import { ISuperchainConfig } from "interfaces/L1/ISuperchainConfig.sol"; ...@@ -23,105 +22,218 @@ import { ISuperchainConfig } from "interfaces/L1/ISuperchainConfig.sol";
/// challenged within the challenge period. By using stored anchor states, new FaultDisputeGame instances can /// challenged within the challenge period. By using stored anchor states, new FaultDisputeGame instances can
/// be initialized with a more recent starting state which reduces the amount of required offchain computation. /// be initialized with a more recent starting state which reduces the amount of required offchain computation.
contract AnchorStateRegistry is Initializable, ISemver { contract AnchorStateRegistry is Initializable, ISemver {
/// @notice Describes an initial anchor state for a game type.
struct StartingAnchorRoot {
GameType gameType;
OutputRoot outputRoot;
}
/// @notice Semantic version. /// @notice Semantic version.
/// @custom:semver 2.0.1-beta.6 /// @custom:semver 2.1.0-beta.1
string public constant version = "2.0.1-beta.6"; string public constant version = "2.1.0-beta.1";
/// @notice DisputeGameFactory address.
IDisputeGameFactory internal immutable DISPUTE_GAME_FACTORY;
/// @notice Returns the anchor state for the given game type.
mapping(GameType => OutputRoot) public anchors;
/// @notice Address of the SuperchainConfig contract. /// @notice Address of the SuperchainConfig contract.
ISuperchainConfig public superchainConfig; ISuperchainConfig public superchainConfig;
/// @param _disputeGameFactory DisputeGameFactory address. /// @notice Address of the DisputeGameFactory contract.
constructor(IDisputeGameFactory _disputeGameFactory) { IDisputeGameFactory public disputeGameFactory;
DISPUTE_GAME_FACTORY = _disputeGameFactory;
/// @notice Address of the OptimismPortal contract.
IOptimismPortal2 public portal;
/// @notice The game whose claim is currently being used as the anchor state.
IFaultDisputeGame public anchorGame;
/// @notice The starting anchor root.
OutputRoot internal startingAnchorRoot;
/// @notice Emitted when an anchor state is not updated.
/// @param game Game that was not used as the new anchor game.
event AnchorNotUpdated(IFaultDisputeGame indexed game);
/// @notice Emitted when an anchor state is updated.
/// @param game Game that was used as the new anchor game.
event AnchorUpdated(IFaultDisputeGame indexed game);
/// @notice Thrown when an unauthorized caller attempts to set the anchor state.
error AnchorStateRegistry_Unauthorized();
/// @notice Thrown when an improper anchor game is provided.
error AnchorStateRegistry_ImproperAnchorGame();
/// @notice Thrown when an invalid anchor game is provided.
error AnchorStateRegistry_InvalidAnchorGame();
/// @notice Constructor to disable initializers.
constructor() {
_disableInitializers(); _disableInitializers();
} }
/// @notice Initializes the contract. /// @notice Initializes the contract.
/// @param _startingAnchorRoots An array of starting anchor roots.
/// @param _superchainConfig The address of the SuperchainConfig contract. /// @param _superchainConfig The address of the SuperchainConfig contract.
/// @param _disputeGameFactory The address of the DisputeGameFactory contract.
/// @param _portal The address of the OptimismPortal contract.
/// @param _startingAnchorRoot The starting anchor root.
function initialize( function initialize(
StartingAnchorRoot[] memory _startingAnchorRoots, ISuperchainConfig _superchainConfig,
ISuperchainConfig _superchainConfig IDisputeGameFactory _disputeGameFactory,
IOptimismPortal2 _portal,
OutputRoot memory _startingAnchorRoot
) )
external external
initializer initializer
{ {
for (uint256 i = 0; i < _startingAnchorRoots.length; i++) {
StartingAnchorRoot memory startingAnchorRoot = _startingAnchorRoots[i];
anchors[startingAnchorRoot.gameType] = startingAnchorRoot.outputRoot;
}
superchainConfig = _superchainConfig; superchainConfig = _superchainConfig;
disputeGameFactory = _disputeGameFactory;
portal = _portal;
startingAnchorRoot = _startingAnchorRoot;
} }
/// @notice Returns the DisputeGameFactory address. /// @custom:legacy
/// @return DisputeGameFactory address. /// @notice Returns the anchor root. Note that this is a legacy deprecated function and will
function disputeGameFactory() external view returns (IDisputeGameFactory) { /// be removed in a future release. Use getAnchorRoot() instead. Anchor roots are no
return DISPUTE_GAME_FACTORY; /// longer stored per game type, so this function will return the same root for all
/// game types.
function anchors(GameType /* unused */ ) external view returns (Hash, uint256) {
return getAnchorRoot();
} }
/// @notice Callable by FaultDisputeGame contracts to update the anchor state. Pulls the anchor state directly from /// @notice Returns the current anchor root.
/// the FaultDisputeGame contract and stores it in the registry if the new anchor state is valid and the /// @return The anchor root.
/// state is newer than the current anchor state. function getAnchorRoot() public view returns (Hash, uint256) {
function tryUpdateAnchorState() external { // Return the starting anchor root if there is no anchor game.
if (address(anchorGame) == address(0)) {
return (startingAnchorRoot.root, startingAnchorRoot.l2BlockNumber);
}
// Otherwise, return the anchor root.
return (Hash.wrap(anchorGame.rootClaim().raw()), anchorGame.l2BlockNumber());
}
/// @notice Determines whether a game is registered in the DisputeGameFactory.
/// @param _game The game to check.
/// @return Whether the game is factory registered.
function isGameRegistered(IDisputeGame _game) public view returns (bool) {
// Grab the game and game data. // Grab the game and game data.
IFaultDisputeGame game = IFaultDisputeGame(msg.sender); (GameType gameType, Claim rootClaim, bytes memory extraData) = _game.gameData();
(GameType gameType, Claim rootClaim, bytes memory extraData) = game.gameData();
// Grab the verified address of the game based on the game data. // Grab the verified address of the game based on the game data.
// slither-disable-next-line unused-return (IDisputeGame _factoryRegisteredGame,) =
(IDisputeGame factoryRegisteredGame,) = disputeGameFactory.games({ _gameType: gameType, _rootClaim: rootClaim, _extraData: extraData });
DISPUTE_GAME_FACTORY.games({ _gameType: gameType, _rootClaim: rootClaim, _extraData: extraData });
// Return whether the game is factory registered.
return address(_factoryRegisteredGame) == address(_game);
}
// Must be a valid game. /// @notice Determines whether a game is of a respected game type.
if (address(factoryRegisteredGame) != address(game)) revert UnregisteredGame(); /// @param _game The game to check.
/// @return Whether the game is of a respected game type.
function isGameRespected(IDisputeGame _game) public view returns (bool) {
return _game.gameType().raw() == portal.respectedGameType().raw();
}
/// @notice Determines whether a game is blacklisted.
/// @param _game The game to check.
/// @return Whether the game is blacklisted.
function isGameBlacklisted(IDisputeGame _game) public view returns (bool) {
return portal.disputeGameBlacklist(_game);
}
// No need to update anything if the anchor state is already newer. /// @notice Determines whether a game is retired.
if (game.l2BlockNumber() <= anchors[gameType].l2BlockNumber) { /// @param _game The game to check.
/// @return Whether the game is retired.
function isGameRetired(IDisputeGame _game) public view returns (bool) {
// Must be created at or after the respectedGameTypeUpdatedAt timestamp. Note that the
// strict inequality exactly mirrors the logic in the OptimismPortal contract.
return _game.createdAt().raw() < portal.respectedGameTypeUpdatedAt();
}
/// @notice **READ THIS FUNCTION DOCUMENTATION CAREFULLY.**
/// Determines whether a game resolved properly and the game was not subject to any
/// invalidation conditions. The root claim of a proper game IS NOT guaranteed to be
/// valid. The root claim of a proper game CAN BE incorrect and still be a proper game.
/// DO NOT USE THIS FUNCTION ALONE TO DETERMINE IF A ROOT CLAIM IS VALID.
/// @dev Note that it is possible for games to be created when their game type is not the
/// respected game type. We do not consider these games to be Proper Games. isGameProper()
/// can currently guarantee this because the OptimismPortal contract will always set the
/// retirement timestamp whenever the respected game type is updated such that any games
/// created before any update of the respected game type are automatically retired. If
/// this coupling is broken, then we must instead check that the game type *was* the
/// respected game type at the time of the game's creation.
/// @param _game The game to check.
/// @return Whether the game is a proper game.
function isGameProper(IDisputeGame _game) public view returns (bool) {
// Must be registered in the DisputeGameFactory.
if (!isGameRegistered(_game)) {
return false;
}
// Must be respected game type.
if (!isGameRespected(_game)) {
return false;
}
// Must not be blacklisted.
if (isGameBlacklisted(_game)) {
return false;
}
// Must be created at or after the respectedGameTypeUpdatedAt timestamp.
if (isGameRetired(_game)) {
return false;
}
return true;
}
/// @notice Allows FaultDisputeGame contracts to attempt to become the new anchor game. A game
/// can only become the new anchor game if it is not invalid (it is a Proper Game), it
/// resolved in favor of the root claim, and it is newer than the current anchor game.
function tryUpdateAnchorState() external {
// Grab the game.
IFaultDisputeGame game = IFaultDisputeGame(msg.sender);
// Check if the game is a proper game.
if (!isGameProper(game)) {
emit AnchorNotUpdated(game);
return; return;
} }
// Must be a game that resolved in favor of the state. // Must be a game that resolved in favor of the state.
if (game.status() != GameStatus.DEFENDER_WINS) { if (game.status() != GameStatus.DEFENDER_WINS) {
emit AnchorNotUpdated(game);
return;
}
// Must be newer than the current anchor game.
(, uint256 anchorL2BlockNumber) = getAnchorRoot();
if (game.l2BlockNumber() <= anchorL2BlockNumber) {
emit AnchorNotUpdated(game);
return; return;
} }
// Actually update the anchor state. // Update the anchor game.
anchors[gameType] = OutputRoot({ l2BlockNumber: game.l2BlockNumber(), root: Hash.wrap(game.rootClaim().raw()) }); anchorGame = game;
emit AnchorUpdated(game);
} }
/// @notice Sets the anchor state given the game. /// @notice Sets the anchor state given the game. Can only be triggered by the Guardian
/// address. Unlike tryUpdateAnchorState(), this function does not check if the
/// provided is newer than the existing anchor game. This allows the Guardian to
/// recover from situations in which the current anchor game is invalid.
/// @param _game The game to set the anchor state for. /// @param _game The game to set the anchor state for.
function setAnchorState(IFaultDisputeGame _game) external { function setAnchorState(IFaultDisputeGame _game) external {
if (msg.sender != superchainConfig.guardian()) revert Unauthorized(); // Function can only be triggered by the guardian.
if (msg.sender != superchainConfig.guardian()) {
// Get the metadata of the game. revert AnchorStateRegistry_Unauthorized();
(GameType gameType, Claim rootClaim, bytes memory extraData) = _game.gameData(); }
// Grab the verified address of the game based on the game data.
// slither-disable-next-line unused-return
(IDisputeGame factoryRegisteredGame,) =
DISPUTE_GAME_FACTORY.games({ _gameType: gameType, _rootClaim: rootClaim, _extraData: extraData });
// Must be a valid game. // Check if the game is a proper game.
if (address(factoryRegisteredGame) != address(_game)) revert UnregisteredGame(); if (!isGameProper(_game)) {
revert AnchorStateRegistry_ImproperAnchorGame();
}
// The game must have resolved in favor of the root claim. // The game must have resolved in favor of the root claim.
if (_game.status() != GameStatus.DEFENDER_WINS) revert InvalidGameStatus(); if (_game.status() != GameStatus.DEFENDER_WINS) {
revert AnchorStateRegistry_InvalidAnchorGame();
}
// Update the anchor. // Update the anchor game.
anchors[gameType] = anchorGame = _game;
OutputRoot({ l2BlockNumber: _game.l2BlockNumber(), root: Hash.wrap(_game.rootClaim().raw()) }); emit AnchorUpdated(_game);
} }
} }
...@@ -127,13 +127,3 @@ error L2BlockNumberChallenged(); ...@@ -127,13 +127,3 @@ error L2BlockNumberChallenged();
/// @notice Thrown when an unauthorized address attempts to interact with the game. /// @notice Thrown when an unauthorized address attempts to interact with the game.
error BadAuth(); error BadAuth();
////////////////////////////////////////////////////////////////
// `AnchorStateRegistry` Errors //
////////////////////////////////////////////////////////////////
/// @notice Thrown when attempting to set an anchor state using an unregistered game.
error UnregisteredGame();
/// @notice Thrown when attempting to set an anchor state using an invalid game result.
error InvalidGameStatus();
...@@ -98,7 +98,7 @@ contract OPContractsManager_Deploy_Test is DeployOPChain_TestBase { ...@@ -98,7 +98,7 @@ contract OPContractsManager_Deploy_Test is DeployOPChain_TestBase {
basefeeScalar: _doi.basefeeScalar(), basefeeScalar: _doi.basefeeScalar(),
blobBasefeeScalar: _doi.blobBaseFeeScalar(), blobBasefeeScalar: _doi.blobBaseFeeScalar(),
l2ChainId: _doi.l2ChainId(), l2ChainId: _doi.l2ChainId(),
startingAnchorRoots: _doi.startingAnchorRoots(), startingAnchorRoot: _doi.startingAnchorRoot(),
saltMixer: _doi.saltMixer(), saltMixer: _doi.saltMixer(),
gasLimit: _doi.gasLimit(), gasLimit: _doi.gasLimit(),
disputeGameType: _doi.disputeGameType(), disputeGameType: _doi.disputeGameType(),
...@@ -184,7 +184,6 @@ contract OPContractsManager_AddGameType_Test is Test { ...@@ -184,7 +184,6 @@ contract OPContractsManager_AddGameType_Test is Test {
(blueprints.proxyAdmin,) = Blueprint.create(vm.getCode("ProxyAdmin"), salt); (blueprints.proxyAdmin,) = Blueprint.create(vm.getCode("ProxyAdmin"), salt);
(blueprints.l1ChugSplashProxy,) = Blueprint.create(vm.getCode("L1ChugSplashProxy"), salt); (blueprints.l1ChugSplashProxy,) = Blueprint.create(vm.getCode("L1ChugSplashProxy"), salt);
(blueprints.resolvedDelegateProxy,) = Blueprint.create(vm.getCode("ResolvedDelegateProxy"), salt); (blueprints.resolvedDelegateProxy,) = Blueprint.create(vm.getCode("ResolvedDelegateProxy"), salt);
(blueprints.anchorStateRegistry,) = Blueprint.create(vm.getCode("AnchorStateRegistry"), salt);
(blueprints.permissionedDisputeGame1, blueprints.permissionedDisputeGame2) = (blueprints.permissionedDisputeGame1, blueprints.permissionedDisputeGame2) =
Blueprint.create(vm.getCode("PermissionedDisputeGame"), salt); Blueprint.create(vm.getCode("PermissionedDisputeGame"), salt);
(blueprints.permissionlessDisputeGame1, blueprints.permissionlessDisputeGame2) = (blueprints.permissionlessDisputeGame1, blueprints.permissionlessDisputeGame2) =
...@@ -200,6 +199,7 @@ contract OPContractsManager_AddGameType_Test is Test { ...@@ -200,6 +199,7 @@ contract OPContractsManager_AddGameType_Test is Test {
l1CrossDomainMessengerImpl: address(new L1CrossDomainMessenger()), l1CrossDomainMessengerImpl: address(new L1CrossDomainMessenger()),
l1StandardBridgeImpl: address(new L1StandardBridge()), l1StandardBridgeImpl: address(new L1StandardBridge()),
disputeGameFactoryImpl: address(new DisputeGameFactory()), disputeGameFactoryImpl: address(new DisputeGameFactory()),
anchorStateRegistryImpl: address(new AnchorStateRegistry()),
delayedWETHImpl: address(new DelayedWETH(3)), delayedWETHImpl: address(new DelayedWETH(3)),
mipsImpl: address(new MIPS(oracle)) mipsImpl: address(new MIPS(oracle))
}); });
...@@ -209,12 +209,6 @@ contract OPContractsManager_AddGameType_Test is Test { ...@@ -209,12 +209,6 @@ contract OPContractsManager_AddGameType_Test is Test {
opcm = new OPContractsManager(superchainConfigProxy, protocolVersionsProxy, "dev", blueprints, impls); opcm = new OPContractsManager(superchainConfigProxy, protocolVersionsProxy, "dev", blueprints, impls);
AnchorStateRegistry.StartingAnchorRoot[] memory roots = new AnchorStateRegistry.StartingAnchorRoot[](1);
roots[0] = AnchorStateRegistry.StartingAnchorRoot({
outputRoot: OutputRoot({ root: Hash.wrap(hex"dead"), l2BlockNumber: 0 }),
gameType: GameType.wrap(1)
});
chainDeployOutput = opcm.deploy( chainDeployOutput = opcm.deploy(
OPContractsManager.DeployInput({ OPContractsManager.DeployInput({
roles: OPContractsManager.Roles({ roles: OPContractsManager.Roles({
...@@ -227,7 +221,7 @@ contract OPContractsManager_AddGameType_Test is Test { ...@@ -227,7 +221,7 @@ contract OPContractsManager_AddGameType_Test is Test {
}), }),
basefeeScalar: 1, basefeeScalar: 1,
blobBasefeeScalar: 1, blobBasefeeScalar: 1,
startingAnchorRoots: abi.encode(roots), startingAnchorRoot: abi.encode(OutputRoot({ root: Hash.wrap(hex"dead"), l2BlockNumber: 0 })),
l2ChainId: 100, l2ChainId: 100,
saltMixer: "hello", saltMixer: "hello",
gasLimit: 30_000_000, gasLimit: 30_000_000,
......
...@@ -5,12 +5,16 @@ pragma solidity ^0.8.15; ...@@ -5,12 +5,16 @@ pragma solidity ^0.8.15;
import { FaultDisputeGame_Init, _changeClaimStatus } from "test/dispute/FaultDisputeGame.t.sol"; import { FaultDisputeGame_Init, _changeClaimStatus } from "test/dispute/FaultDisputeGame.t.sol";
// Libraries // Libraries
import "src/dispute/lib/Types.sol"; import { GameType, GameStatus, Hash, Claim, VMStatuses, OutputRoot } from "src/dispute/lib/Types.sol";
import "src/dispute/lib/Errors.sol";
import { Hash } from "src/dispute/lib/Types.sol"; // Interfaces
import { IFaultDisputeGame } from "interfaces/dispute/IFaultDisputeGame.sol";
import { IAnchorStateRegistry } from "interfaces/dispute/IAnchorStateRegistry.sol";
contract AnchorStateRegistry_Init is FaultDisputeGame_Init { contract AnchorStateRegistry_Init is FaultDisputeGame_Init {
event AnchorNotUpdated(IFaultDisputeGame indexed game);
event AnchorUpdated(IFaultDisputeGame indexed game);
function setUp() public virtual override { function setUp() public virtual override {
// Duplicating the initialization/setup logic of FaultDisputeGame_Test. // Duplicating the initialization/setup logic of FaultDisputeGame_Test.
// See that test for more information, actual values here not really important. // See that test for more information, actual values here not really important.
...@@ -26,89 +30,608 @@ contract AnchorStateRegistry_Init is FaultDisputeGame_Init { ...@@ -26,89 +30,608 @@ contract AnchorStateRegistry_Init is FaultDisputeGame_Init {
contract AnchorStateRegistry_Initialize_Test is AnchorStateRegistry_Init { contract AnchorStateRegistry_Initialize_Test is AnchorStateRegistry_Init {
/// @dev Tests that initialization is successful. /// @dev Tests that initialization is successful.
function test_initialize_succeeds() public view { function test_initialize_succeeds() public view {
(Hash cannonRoot, uint256 cannonL2BlockNumber) = anchorStateRegistry.anchors(GameTypes.CANNON); // Verify starting anchor root.
(Hash permissionedCannonRoot, uint256 permissionedCannonL2BlockNumber) = (Hash root, uint256 l2BlockNumber) = anchorStateRegistry.getAnchorRoot();
anchorStateRegistry.anchors(GameTypes.PERMISSIONED_CANNON); assertEq(root.raw(), 0xDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF);
(Hash asteriscRoot, uint256 asteriscL2BlockNumber) = anchorStateRegistry.anchors(GameTypes.ASTERISC); assertEq(l2BlockNumber, 0);
(Hash alphabetRoot, uint256 alphabetL2BlockNumber) = anchorStateRegistry.anchors(GameTypes.ALPHABET);
assertEq(cannonRoot.raw(), 0xDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF); // Verify contract addresses.
assertEq(cannonL2BlockNumber, 0); assert(anchorStateRegistry.superchainConfig() == superchainConfig);
assertEq(permissionedCannonRoot.raw(), 0xDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF); assert(anchorStateRegistry.disputeGameFactory() == disputeGameFactory);
assertEq(permissionedCannonL2BlockNumber, 0); assert(anchorStateRegistry.portal() == optimismPortal2);
assertEq(asteriscRoot.raw(), 0xDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF); }
assertEq(asteriscL2BlockNumber, 0); }
assertEq(alphabetRoot.raw(), 0xDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF);
assertEq(alphabetL2BlockNumber, 0); contract AnchorStateRegistry_Initialize_TestFail is AnchorStateRegistry_Init {
/// @notice Tests that initialization cannot be done twice
function test_initialize_twice_reverts() public {
vm.expectRevert("Initializable: contract is already initialized");
anchorStateRegistry.initialize(
superchainConfig,
disputeGameFactory,
optimismPortal2,
OutputRoot({
root: Hash.wrap(0xDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF),
l2BlockNumber: 0
})
);
}
}
contract AnchorStateRegistry_Version_Test is AnchorStateRegistry_Init {
/// @notice Tests that the version function returns a string.
function test_version_succeeds() public view {
assert(bytes(anchorStateRegistry.version()).length > 0);
}
}
contract AnchorStateRegistry_GetAnchorRoot_Test is AnchorStateRegistry_Init {
/// @notice Tests that getAnchorRoot will return the value of the starting anchor root when no
/// anchor game exists yet.
function test_getAnchorRoot_noAnchorGame_succeeds() public view {
// Assert that we nave no anchor game yet.
assert(address(anchorStateRegistry.anchorGame()) == address(0));
// We should get the starting anchor root back.
(Hash root, uint256 l2BlockNumber) = anchorStateRegistry.getAnchorRoot();
assertEq(root.raw(), 0xDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF);
assertEq(l2BlockNumber, 0);
}
}
contract AnchorStateRegistry_Anchors_Test is AnchorStateRegistry_Init {
/// @notice Tests that the anchors() function always matches the result of the getAnchorRoot()
/// function regardless of the game type used.
/// @param _gameType Game type to use as input to anchors().
function testFuzz_anchors_matchesGetAnchorRoot_succeeds(GameType _gameType) public view {
// Get the anchor root according to getAnchorRoot().
(Hash root1, uint256 l2BlockNumber1) = anchorStateRegistry.getAnchorRoot();
// Get the anchor root according to anchors().
(Hash root2, uint256 l2BlockNumber2) = anchorStateRegistry.anchors(_gameType);
// Assert that the two roots are the same.
assertEq(root1.raw(), root2.raw());
assertEq(l2BlockNumber1, l2BlockNumber2);
}
}
contract AnchorStateRegistry_IsGameRegistered_Test is AnchorStateRegistry_Init {
/// @notice Tests that isGameRegistered will return true if the game is registered.
function test_isGameRegistered_isActuallyFactoryRegistered_succeeds() public view {
assertTrue(anchorStateRegistry.isGameRegistered(gameProxy));
}
/// @notice Tests that isGameRegistered will return false if the game is not registered.
function test_isGameRegistered_isNotFactoryRegistered_succeeds() public {
// Mock the DisputeGameFactory to make it seem that the game was not registered.
vm.mockCall(
address(disputeGameFactory),
abi.encodeCall(
disputeGameFactory.games, (gameProxy.gameType(), gameProxy.rootClaim(), gameProxy.extraData())
),
abi.encode(address(0), 0)
);
assertFalse(anchorStateRegistry.isGameRegistered(gameProxy));
}
}
contract AnchorStateRegistry_IsGameBlacklisted_Test is AnchorStateRegistry_Init {
/// @notice Tests that isGameBlacklisted will return true if the game is blacklisted.
function test_isGameBlacklisted_isActuallyBlacklisted_succeeds() public {
// Mock the disputeGameBlacklist call to return true.
vm.mockCall(
address(optimismPortal2),
abi.encodeCall(optimismPortal2.disputeGameBlacklist, (gameProxy)),
abi.encode(true)
);
assertTrue(anchorStateRegistry.isGameBlacklisted(gameProxy));
}
/// @notice Tests that isGameBlacklisted will return false if the game is not blacklisted.
function test_isGameBlacklisted_isNotBlacklisted_succeeds() public {
// Mock the disputeGameBlacklist call to return false.
vm.mockCall(
address(optimismPortal2),
abi.encodeCall(optimismPortal2.disputeGameBlacklist, (gameProxy)),
abi.encode(false)
);
assertFalse(anchorStateRegistry.isGameBlacklisted(gameProxy));
}
}
contract AnchorStateRegistry_IsGameRespected_Test is AnchorStateRegistry_Init {
/// @notice Tests that isGameRespected will return true if the game is of the respected game type.
function test_isGameRespected_isRespected_succeeds() public {
// Make our game type the respected game type.
vm.mockCall(
address(optimismPortal2),
abi.encodeCall(optimismPortal2.respectedGameType, ()),
abi.encode(gameProxy.gameType())
);
assertTrue(anchorStateRegistry.isGameRespected(gameProxy));
}
/// @notice Tests that isGameRespected will return false if the game is not of the respected game
/// type.
/// @param _gameType The game type to use for the test.
function testFuzz_isGameRespected_isNotRespected_succeeds(GameType _gameType) public {
if (_gameType.raw() == gameProxy.gameType().raw()) {
_gameType = GameType.wrap(_gameType.raw() + 1);
}
// Make our game type NOT the respected game type.
vm.mockCall(
address(optimismPortal2), abi.encodeCall(optimismPortal2.respectedGameType, ()), abi.encode(_gameType)
);
assertFalse(anchorStateRegistry.isGameRespected(gameProxy));
}
}
contract AnchorStateRegistry_IsGameRetired_Test is AnchorStateRegistry_Init {
/// @notice Tests that isGameRetired will return true if the game is retired.
/// @param _retirementTimestamp The retirement timestamp to use for the test.
function testFuzz_isGameRetired_isRetired_succeeds(uint64 _retirementTimestamp) public {
// Make sure retirement timestamp is later than the game's creation time.
_retirementTimestamp = uint64(bound(_retirementTimestamp, gameProxy.createdAt().raw() + 1, type(uint64).max));
// Mock the respectedGameTypeUpdatedAt call to be later than the game's creation time.
vm.mockCall(
address(optimismPortal2),
abi.encodeCall(optimismPortal2.respectedGameTypeUpdatedAt, ()),
abi.encode(_retirementTimestamp)
);
assertTrue(anchorStateRegistry.isGameRetired(gameProxy));
}
/// @notice Tests that isGameRetired will return false if the game is not retired.
/// @param _retirementTimestamp The retirement timestamp to use for the test.
function testFuzz_isGameRetired_isNotRetired_succeeds(uint64 _retirementTimestamp) public {
// Make sure retirement timestamp is earlier than the game's creation time.
_retirementTimestamp = uint64(bound(_retirementTimestamp, 0, gameProxy.createdAt().raw()));
// Mock the respectedGameTypeUpdatedAt call to be earlier than the game's creation time.
vm.mockCall(
address(optimismPortal2),
abi.encodeCall(optimismPortal2.respectedGameTypeUpdatedAt, ()),
abi.encode(_retirementTimestamp)
);
assertFalse(anchorStateRegistry.isGameRetired(gameProxy));
}
}
contract AnchorStateRegistry_IsGameProper_Test is AnchorStateRegistry_Init {
/// @notice Tests that isGameProper will return true if the game meets all conditions.
function test_isGameProper_meetsAllConditions_succeeds() public {
// Make our game type the respected game type.
vm.mockCall(
address(optimismPortal2),
abi.encodeCall(optimismPortal2.respectedGameType, ()),
abi.encode(gameProxy.gameType())
);
assertTrue(anchorStateRegistry.isGameProper(gameProxy));
}
/// @notice Tests that isGameProper will return false if the game is not registered.
function test_isGameProper_isNotFactoryRegistered_succeeds() public {
// Mock the DisputeGameFactory to make it seem that the game was not registered.
vm.mockCall(
address(disputeGameFactory),
abi.encodeCall(
disputeGameFactory.games, (gameProxy.gameType(), gameProxy.rootClaim(), gameProxy.extraData())
),
abi.encode(address(0), 0)
);
assertFalse(anchorStateRegistry.isGameProper(gameProxy));
}
/// @notice Tests that isGameProper will return false if the game is not the respected game type.
function testFuzz_isGameProper_isNotRespected_succeeds(GameType _gameType) public {
if (_gameType.raw() == gameProxy.gameType().raw()) {
_gameType = GameType.wrap(_gameType.raw() + 1);
}
// Make our game type NOT the respected game type.
vm.mockCall(
address(optimismPortal2), abi.encodeCall(optimismPortal2.respectedGameType, ()), abi.encode(_gameType)
);
assertFalse(anchorStateRegistry.isGameProper(gameProxy));
}
/// @notice Tests that isGameProper will return false if the game is blacklisted.
function test_isGameProper_isBlacklisted_succeeds() public {
// Mock the disputeGameBlacklist call to return true.
vm.mockCall(
address(optimismPortal2),
abi.encodeCall(optimismPortal2.disputeGameBlacklist, (gameProxy)),
abi.encode(true)
);
assertFalse(anchorStateRegistry.isGameProper(gameProxy));
}
/// @notice Tests that isGameProper will return false if the game is retired.
function testFuzz_isGameProper_isRetired_succeeds(uint64 _retirementTimestamp) public {
// Make sure retirement timestamp is later than the game's creation time.
_retirementTimestamp = uint64(bound(_retirementTimestamp, gameProxy.createdAt().raw() + 1, type(uint64).max));
// Mock the respectedGameTypeUpdatedAt call to be later than the game's creation time.
vm.mockCall(
address(optimismPortal2),
abi.encodeCall(optimismPortal2.respectedGameTypeUpdatedAt, ()),
abi.encode(_retirementTimestamp)
);
assertFalse(anchorStateRegistry.isGameProper(gameProxy));
} }
} }
contract AnchorStateRegistry_TryUpdateAnchorState_Test is AnchorStateRegistry_Init { contract AnchorStateRegistry_TryUpdateAnchorState_Test is AnchorStateRegistry_Init {
/// @dev Tests that updating the anchor state succeeds when the game state is valid and newer. /// @notice Tests that tryUpdateAnchorState will succeed if the game is valid, the game block
function test_tryUpdateAnchorState_validNewerState_succeeds() public { /// number is greater than the current anchor root block number, and the game is the
// Confirm that the anchor state is older than the game state. /// currently respected game type.
(Hash root, uint256 l2BlockNumber) = anchorStateRegistry.anchors(gameProxy.gameType()); /// @param _l2BlockNumber The L2 block number to use for the game.
assert(l2BlockNumber < gameProxy.l2BlockNumber()); function testFuzz_tryUpdateAnchorState_validNewerState_succeeds(uint256 _l2BlockNumber) public {
// Grab block number of the existing anchor root.
(Hash root, uint256 l2BlockNumber) = anchorStateRegistry.getAnchorRoot();
// Bound the new block number.
_l2BlockNumber = bound(_l2BlockNumber, l2BlockNumber + 1, type(uint256).max);
// Mock the l2BlockNumber call.
vm.mockCall(address(gameProxy), abi.encodeCall(gameProxy.l2BlockNumber, ()), abi.encode(_l2BlockNumber));
// Mock the state that we want. // Mock the DEFENDER_WINS state.
vm.mockCall(address(gameProxy), abi.encodeCall(gameProxy.status, ()), abi.encode(GameStatus.DEFENDER_WINS)); vm.mockCall(address(gameProxy), abi.encodeCall(gameProxy.status, ()), abi.encode(GameStatus.DEFENDER_WINS));
// Try to update the anchor state. // Make our game type the respected game type.
vm.mockCall(
address(optimismPortal2),
abi.encodeCall(optimismPortal2.respectedGameType, ()),
abi.encode(gameProxy.gameType())
);
// Update the anchor state.
vm.prank(address(gameProxy)); vm.prank(address(gameProxy));
vm.expectEmit(address(anchorStateRegistry));
emit AnchorUpdated(gameProxy);
anchorStateRegistry.tryUpdateAnchorState(); anchorStateRegistry.tryUpdateAnchorState();
// Confirm that the anchor state is now the same as the game state. // Confirm that the anchor state is now the same as the game state.
(root, l2BlockNumber) = anchorStateRegistry.anchors(gameProxy.gameType()); (root, l2BlockNumber) = anchorStateRegistry.getAnchorRoot();
assertEq(l2BlockNumber, gameProxy.l2BlockNumber()); assertEq(l2BlockNumber, gameProxy.l2BlockNumber());
assertEq(root.raw(), gameProxy.rootClaim().raw()); assertEq(root.raw(), gameProxy.rootClaim().raw());
// Confirm that the anchor game is now set.
IFaultDisputeGame anchorGame = anchorStateRegistry.anchorGame();
assertEq(address(anchorGame), address(gameProxy));
} }
/// @dev Tests that updating the anchor state fails when the game state is valid but older. /// @notice Tests that tryUpdateAnchorState will not update the anchor state if the game block
function test_tryUpdateAnchorState_validOlderState_fails() public { /// number is less than or equal to the current anchor root block number.
// Confirm that the anchor state is newer than the game state. /// @param _l2BlockNumber The L2 block number to use for the game.
vm.mockCall(address(gameProxy), abi.encodeCall(gameProxy.l2BlockNumber, ()), abi.encode(0)); function testFuzz_tryUpdateAnchorState_validOlderStateNoUpdate_succeeds(uint256 _l2BlockNumber) public {
(Hash root, uint256 l2BlockNumber) = anchorStateRegistry.anchors(gameProxy.gameType()); // Grab block number of the existing anchor root.
assert(l2BlockNumber >= gameProxy.l2BlockNumber()); (Hash root, uint256 l2BlockNumber) = anchorStateRegistry.getAnchorRoot();
// Bound the new block number.
_l2BlockNumber = bound(_l2BlockNumber, 0, l2BlockNumber);
// Mock the state that we want. // Mock the l2BlockNumber call.
vm.mockCall(address(gameProxy), abi.encodeCall(gameProxy.l2BlockNumber, ()), abi.encode(0)); vm.mockCall(address(gameProxy), abi.encodeCall(gameProxy.l2BlockNumber, ()), abi.encode(_l2BlockNumber));
// Mock the DEFENDER_WINS state.
vm.mockCall(address(gameProxy), abi.encodeCall(gameProxy.status, ()), abi.encode(GameStatus.DEFENDER_WINS)); vm.mockCall(address(gameProxy), abi.encodeCall(gameProxy.status, ()), abi.encode(GameStatus.DEFENDER_WINS));
// Make our game type the respected game type.
vm.mockCall(
address(optimismPortal2),
abi.encodeCall(optimismPortal2.respectedGameType, ()),
abi.encode(gameProxy.gameType())
);
// Try to update the anchor state. // Try to update the anchor state.
vm.prank(address(gameProxy)); vm.prank(address(gameProxy));
vm.expectEmit(address(anchorStateRegistry));
emit AnchorNotUpdated(gameProxy);
anchorStateRegistry.tryUpdateAnchorState(); anchorStateRegistry.tryUpdateAnchorState();
// Confirm that the anchor state has not updated. // Confirm that the anchor state has not updated.
(Hash updatedRoot, uint256 updatedL2BlockNumber) = anchorStateRegistry.anchors(gameProxy.gameType()); (Hash updatedRoot, uint256 updatedL2BlockNumber) = anchorStateRegistry.getAnchorRoot();
assertEq(updatedL2BlockNumber, l2BlockNumber); assertEq(updatedL2BlockNumber, l2BlockNumber);
assertEq(updatedRoot.raw(), root.raw()); assertEq(updatedRoot.raw(), root.raw());
} }
/// @dev Tests that updating the anchor state fails when the game state is invalid. /// @notice Tests that tryUpdateAnchorState will not update the anchor state if the game is not
function test_tryUpdateAnchorState_invalidNewerState_fails() public { /// registered.
// Confirm that the anchor state is older than the game state. /// @param _l2BlockNumber The L2 block number to use for the game.
(Hash root, uint256 l2BlockNumber) = anchorStateRegistry.anchors(gameProxy.gameType()); function testFuzz_tryUpdateAnchorState_notFactoryRegisteredGameNoUpdate_succeeds(uint256 _l2BlockNumber) public {
assert(l2BlockNumber < gameProxy.l2BlockNumber()); // Grab block number of the existing anchor root.
(Hash root, uint256 l2BlockNumber) = anchorStateRegistry.getAnchorRoot();
// Bound the new block number.
_l2BlockNumber = bound(_l2BlockNumber, l2BlockNumber, type(uint256).max);
// Mock the l2BlockNumber call.
vm.mockCall(address(gameProxy), abi.encodeCall(gameProxy.l2BlockNumber, ()), abi.encode(_l2BlockNumber));
// Mock the DEFENDER_WINS state.
vm.mockCall(address(gameProxy), abi.encodeCall(gameProxy.status, ()), abi.encode(GameStatus.DEFENDER_WINS));
// Mock the state that we want. // Make our game type the respected game type.
vm.mockCall(
address(optimismPortal2),
abi.encodeCall(optimismPortal2.respectedGameType, ()),
abi.encode(gameProxy.gameType())
);
// Mock the DisputeGameFactory to make it seem that the game was not registered.
vm.mockCall(
address(disputeGameFactory),
abi.encodeCall(
disputeGameFactory.games, (gameProxy.gameType(), gameProxy.rootClaim(), gameProxy.extraData())
),
abi.encode(address(0), 0)
);
// Try to update the anchor state.
vm.prank(address(gameProxy));
vm.expectEmit(address(anchorStateRegistry));
emit AnchorNotUpdated(gameProxy);
anchorStateRegistry.tryUpdateAnchorState();
// Confirm that the anchor state has not updated.
(Hash updatedRoot, uint256 updatedL2BlockNumber) = anchorStateRegistry.getAnchorRoot();
assertEq(updatedL2BlockNumber, l2BlockNumber);
assertEq(updatedRoot.raw(), root.raw());
}
/// @notice Tests that tryUpdateAnchorState will not update the anchor state if the game status
/// is CHALLENGER_WINS.
/// @param _l2BlockNumber The L2 block number to use for the game.
function testFuzz_tryUpdateAnchorState_challengerWinsNoUpdate_succeeds(uint256 _l2BlockNumber) public {
// Grab block number of the existing anchor root.
(Hash root, uint256 l2BlockNumber) = anchorStateRegistry.getAnchorRoot();
// Bound the new block number.
_l2BlockNumber = bound(_l2BlockNumber, l2BlockNumber, type(uint256).max);
// Mock the l2BlockNumber call.
vm.mockCall(address(gameProxy), abi.encodeCall(gameProxy.l2BlockNumber, ()), abi.encode(_l2BlockNumber));
// Mock the CHALLENGER_WINS state.
vm.mockCall(address(gameProxy), abi.encodeCall(gameProxy.status, ()), abi.encode(GameStatus.CHALLENGER_WINS)); vm.mockCall(address(gameProxy), abi.encodeCall(gameProxy.status, ()), abi.encode(GameStatus.CHALLENGER_WINS));
// Make our game type the respected game type.
vm.mockCall(
address(optimismPortal2),
abi.encodeCall(optimismPortal2.respectedGameType, ()),
abi.encode(gameProxy.gameType())
);
// Try to update the anchor state. // Try to update the anchor state.
vm.prank(address(gameProxy)); vm.prank(address(gameProxy));
vm.expectEmit(address(anchorStateRegistry));
emit AnchorNotUpdated(gameProxy);
anchorStateRegistry.tryUpdateAnchorState(); anchorStateRegistry.tryUpdateAnchorState();
// Confirm that the anchor state has not updated. // Confirm that the anchor state has not updated.
(Hash updatedRoot, uint256 updatedL2BlockNumber) = anchorStateRegistry.anchors(gameProxy.gameType()); (Hash updatedRoot, uint256 updatedL2BlockNumber) = anchorStateRegistry.getAnchorRoot();
assertEq(updatedL2BlockNumber, l2BlockNumber);
assertEq(updatedRoot.raw(), root.raw());
}
/// @notice Tests that tryUpdateAnchorState will not update the anchor state if the game status
/// is IN_PROGRESS.
/// @param _l2BlockNumber The L2 block number to use for the game.
function testFuzz_tryUpdateAnchorState_inProgressNoUpdate_succeeds(uint256 _l2BlockNumber) public {
// Grab block number of the existing anchor root.
(Hash root, uint256 l2BlockNumber) = anchorStateRegistry.getAnchorRoot();
// Bound the new block number.
_l2BlockNumber = bound(_l2BlockNumber, l2BlockNumber, type(uint256).max);
// Mock the l2BlockNumber call.
vm.mockCall(address(gameProxy), abi.encodeCall(gameProxy.l2BlockNumber, ()), abi.encode(_l2BlockNumber));
// Mock the CHALLENGER_WINS state.
vm.mockCall(address(gameProxy), abi.encodeCall(gameProxy.status, ()), abi.encode(GameStatus.IN_PROGRESS));
// Make our game type the respected game type.
vm.mockCall(
address(optimismPortal2),
abi.encodeCall(optimismPortal2.respectedGameType, ()),
abi.encode(gameProxy.gameType())
);
// Try to update the anchor state.
vm.prank(address(gameProxy));
vm.expectEmit(address(anchorStateRegistry));
emit AnchorNotUpdated(gameProxy);
anchorStateRegistry.tryUpdateAnchorState();
// Confirm that the anchor state has not updated.
(Hash updatedRoot, uint256 updatedL2BlockNumber) = anchorStateRegistry.getAnchorRoot();
assertEq(updatedL2BlockNumber, l2BlockNumber); assertEq(updatedL2BlockNumber, l2BlockNumber);
assertEq(updatedRoot.raw(), root.raw()); assertEq(updatedRoot.raw(), root.raw());
} }
/// @dev Tests that updating the anchor state fails when the game is not registered with the factory. /// @notice Tests that tryUpdateAnchorState will not update the anchor state if the game type
function test_tryUpdateAnchorState_invalidGame_fails() public { /// is not the respected game type.
// Confirm that the anchor state is older than the game state. /// @param _l2BlockNumber The L2 block number to use for the game.
function testFuzz_tryUpdateAnchorState_notRespectedGameTypeNoUpdate_succeeds(uint256 _l2BlockNumber) public {
// Grab block number of the existing anchor root.
(Hash root, uint256 l2BlockNumber) = anchorStateRegistry.anchors(gameProxy.gameType()); (Hash root, uint256 l2BlockNumber) = anchorStateRegistry.anchors(gameProxy.gameType());
assert(l2BlockNumber < gameProxy.l2BlockNumber());
// Mock the state that we want. // Bound the new block number.
_l2BlockNumber = bound(_l2BlockNumber, l2BlockNumber, type(uint256).max);
// Mock the l2BlockNumber call.
vm.mockCall(address(gameProxy), abi.encodeCall(gameProxy.l2BlockNumber, ()), abi.encode(_l2BlockNumber));
// Mock the DEFENDER_WINS state.
vm.mockCall(address(gameProxy), abi.encodeCall(gameProxy.status, ()), abi.encode(GameStatus.DEFENDER_WINS));
// Mock the respectedGameType call so that it does NOT match our game type.
vm.mockCall(address(optimismPortal2), abi.encodeCall(optimismPortal2.respectedGameType, ()), abi.encode(999));
// Try to update the anchor state.
vm.prank(address(gameProxy));
vm.expectEmit(address(anchorStateRegistry));
emit AnchorNotUpdated(gameProxy);
anchorStateRegistry.tryUpdateAnchorState();
// Confirm that the anchor state has not updated.
(Hash updatedRoot, uint256 updatedL2BlockNumber) = anchorStateRegistry.anchors(gameProxy.gameType());
assertEq(updatedL2BlockNumber, l2BlockNumber);
assertEq(updatedRoot.raw(), root.raw());
}
/// @notice Tests that tryUpdateAnchorState will not update the anchor state if the game is
/// blacklisted.
/// @param _l2BlockNumber The L2 block number to use for the game.
function testFuzz_tryUpdateAnchorState_blacklistedGameNoUpdate_succeeds(uint256 _l2BlockNumber) public {
// Grab block number of the existing anchor root.
(Hash root, uint256 l2BlockNumber) = anchorStateRegistry.getAnchorRoot();
// Bound the new block number.
_l2BlockNumber = bound(_l2BlockNumber, l2BlockNumber + 1, type(uint256).max);
// Mock the l2BlockNumber call.
vm.mockCall(address(gameProxy), abi.encodeCall(gameProxy.l2BlockNumber, ()), abi.encode(_l2BlockNumber));
// Mock the DEFENDER_WINS state.
vm.mockCall(address(gameProxy), abi.encodeCall(gameProxy.status, ()), abi.encode(GameStatus.DEFENDER_WINS));
// Make our game type the respected game type.
vm.mockCall(
address(optimismPortal2),
abi.encodeCall(optimismPortal2.respectedGameType, ()),
abi.encode(gameProxy.gameType())
);
// Mock the disputeGameBlacklist call to return true.
vm.mockCall(
address(optimismPortal2),
abi.encodeCall(optimismPortal2.disputeGameBlacklist, (gameProxy)),
abi.encode(true)
);
// Update the anchor state.
vm.prank(address(gameProxy));
vm.expectEmit(address(anchorStateRegistry));
emit AnchorNotUpdated(gameProxy);
anchorStateRegistry.tryUpdateAnchorState();
// Confirm that the anchor state has not updated.
(Hash updatedRoot, uint256 updatedL2BlockNumber) = anchorStateRegistry.anchors(gameProxy.gameType());
assertEq(updatedL2BlockNumber, l2BlockNumber);
assertEq(updatedRoot.raw(), root.raw());
}
/// @notice Tests that tryUpdateAnchorState will not update the anchor state if the game is
/// retired.
/// @param _l2BlockNumber The L2 block number to use for the game.
function testFuzz_tryUpdateAnchorState_retiredGameNoUpdate_succeeds(uint256 _l2BlockNumber) public {
// Grab block number of the existing anchor root.
(Hash root, uint256 l2BlockNumber) = anchorStateRegistry.getAnchorRoot();
// Bound the new block number.
_l2BlockNumber = bound(_l2BlockNumber, l2BlockNumber + 1, type(uint256).max);
// Mock the l2BlockNumber call.
vm.mockCall(address(gameProxy), abi.encodeCall(gameProxy.l2BlockNumber, ()), abi.encode(_l2BlockNumber));
// Mock the DEFENDER_WINS state.
vm.mockCall(address(gameProxy), abi.encodeCall(gameProxy.status, ()), abi.encode(GameStatus.DEFENDER_WINS));
// Make our game type the respected game type.
vm.mockCall(
address(optimismPortal2),
abi.encodeCall(optimismPortal2.respectedGameType, ()),
abi.encode(gameProxy.gameType())
);
// Mock the respectedGameTypeUpdatedAt call to be later than the game's creation time.
vm.mockCall(
address(optimismPortal2),
abi.encodeCall(optimismPortal2.respectedGameTypeUpdatedAt, ()),
abi.encode(gameProxy.createdAt().raw() + 1)
);
// Update the anchor state.
vm.prank(address(gameProxy));
vm.expectEmit(address(anchorStateRegistry));
emit AnchorNotUpdated(gameProxy);
anchorStateRegistry.tryUpdateAnchorState();
// Confirm that the anchor state has not updated.
(Hash updatedRoot, uint256 updatedL2BlockNumber) = anchorStateRegistry.anchors(gameProxy.gameType());
assertEq(updatedL2BlockNumber, l2BlockNumber);
assertEq(updatedRoot.raw(), root.raw());
}
}
contract AnchorStateRegistry_SetAnchorState_Test is AnchorStateRegistry_Init {
/// @notice Tests that setAnchorState will succeed with a game with any L2 block number as long
/// as the game is valid and is the currently respected game type.
/// @param _l2BlockNumber The L2 block number to use for the game.
function testFuzz_setAnchorState_anyL2BlockNumber_succeeds(uint256 _l2BlockNumber) public {
// Mock the l2BlockNumber call.
vm.mockCall(address(gameProxy), abi.encodeCall(gameProxy.l2BlockNumber, ()), abi.encode(_l2BlockNumber));
// Mock the DEFENDER_WINS state.
vm.mockCall(address(gameProxy), abi.encodeCall(gameProxy.status, ()), abi.encode(GameStatus.DEFENDER_WINS));
// Make our game type the respected game type.
vm.mockCall(
address(optimismPortal2),
abi.encodeCall(optimismPortal2.respectedGameType, ()),
abi.encode(gameProxy.gameType())
);
// Set the anchor state.
vm.prank(superchainConfig.guardian());
vm.expectEmit(address(anchorStateRegistry));
emit AnchorUpdated(gameProxy);
anchorStateRegistry.setAnchorState(gameProxy);
// Confirm that the anchor state has updated.
(Hash updatedRoot, uint256 updatedL2BlockNumber) = anchorStateRegistry.anchors(gameProxy.gameType());
assertEq(updatedL2BlockNumber, gameProxy.l2BlockNumber());
assertEq(updatedRoot.raw(), gameProxy.rootClaim().raw());
// Confirm that the anchor game is now set.
IFaultDisputeGame anchorGame = anchorStateRegistry.anchorGame();
assertEq(address(anchorGame), address(gameProxy));
}
}
contract AnchorStateRegistry_SetAnchorState_TestFail is AnchorStateRegistry_Init {
/// @notice Tests that setAnchorState will revert if the sender is not the guardian.
/// @param _sender The address of the sender.
/// @param _l2BlockNumber The L2 block number to use for the game.
function testFuzz_setAnchorState_notGuardian_fails(address _sender, uint256 _l2BlockNumber) public {
// Grab block number of the existing anchor root.
(Hash root, uint256 l2BlockNumber) = anchorStateRegistry.getAnchorRoot();
// Mock the l2BlockNumber call.
vm.mockCall(address(gameProxy), abi.encodeCall(gameProxy.l2BlockNumber, ()), abi.encode(_l2BlockNumber));
// Mock the DEFENDER_WINS state.
vm.mockCall(address(gameProxy), abi.encodeCall(gameProxy.status, ()), abi.encode(GameStatus.DEFENDER_WINS));
// Make our game type the respected game type.
vm.mockCall(
address(optimismPortal2),
abi.encodeCall(optimismPortal2.respectedGameType, ()),
abi.encode(gameProxy.gameType())
);
// Mock the DisputeGameFactory to make it seem that the game was not registered.
vm.mockCall( vm.mockCall(
address(disputeGameFactory), address(disputeGameFactory),
abi.encodeCall( abi.encodeCall(
...@@ -118,9 +641,9 @@ contract AnchorStateRegistry_TryUpdateAnchorState_Test is AnchorStateRegistry_In ...@@ -118,9 +641,9 @@ contract AnchorStateRegistry_TryUpdateAnchorState_Test is AnchorStateRegistry_In
); );
// Try to update the anchor state. // Try to update the anchor state.
vm.prank(address(gameProxy)); vm.prank(_sender);
vm.expectRevert(UnregisteredGame.selector); vm.expectRevert(IAnchorStateRegistry.AnchorStateRegistry_Unauthorized.selector);
anchorStateRegistry.tryUpdateAnchorState(); anchorStateRegistry.setAnchorState(gameProxy);
// Confirm that the anchor state has not updated. // Confirm that the anchor state has not updated.
(Hash updatedRoot, uint256 updatedL2BlockNumber) = anchorStateRegistry.anchors(gameProxy.gameType()); (Hash updatedRoot, uint256 updatedL2BlockNumber) = anchorStateRegistry.anchors(gameProxy.gameType());
...@@ -128,15 +651,26 @@ contract AnchorStateRegistry_TryUpdateAnchorState_Test is AnchorStateRegistry_In ...@@ -128,15 +651,26 @@ contract AnchorStateRegistry_TryUpdateAnchorState_Test is AnchorStateRegistry_In
assertEq(updatedRoot.raw(), root.raw()); assertEq(updatedRoot.raw(), root.raw());
} }
function test_setAnchorState_invalidGame_fails() public { /// @notice Tests that setAnchorState will revert if the game is not registered.
// Confirm that the anchor state is older than the game state. /// @param _l2BlockNumber The L2 block number to use for the game.
(Hash root, uint256 l2BlockNumber) = anchorStateRegistry.anchors(gameProxy.gameType()); function testFuzz_setAnchorState_notFactoryRegisteredGame_fails(uint256 _l2BlockNumber) public {
require( // Grab block number of the existing anchor root.
l2BlockNumber < gameProxy.l2BlockNumber(), (Hash root, uint256 l2BlockNumber) = anchorStateRegistry.getAnchorRoot();
"AnchorStateRegistry_TryUpdateAnchorState_Test: l2BlockNumber < gameProxy.l2BlockNumber()"
// Mock the l2BlockNumber call.
vm.mockCall(address(gameProxy), abi.encodeCall(gameProxy.l2BlockNumber, ()), abi.encode(_l2BlockNumber));
// Mock the DEFENDER_WINS state.
vm.mockCall(address(gameProxy), abi.encodeCall(gameProxy.status, ()), abi.encode(GameStatus.DEFENDER_WINS));
// Make our game type the respected game type.
vm.mockCall(
address(optimismPortal2),
abi.encodeCall(optimismPortal2.respectedGameType, ()),
abi.encode(gameProxy.gameType())
); );
// Mock the state that we want. // Mock the DisputeGameFactory to make it seem that the game was not registered.
vm.mockCall( vm.mockCall(
address(disputeGameFactory), address(disputeGameFactory),
abi.encodeCall( abi.encodeCall(
...@@ -147,7 +681,7 @@ contract AnchorStateRegistry_TryUpdateAnchorState_Test is AnchorStateRegistry_In ...@@ -147,7 +681,7 @@ contract AnchorStateRegistry_TryUpdateAnchorState_Test is AnchorStateRegistry_In
// Try to update the anchor state. // Try to update the anchor state.
vm.prank(superchainConfig.guardian()); vm.prank(superchainConfig.guardian());
vm.expectRevert(UnregisteredGame.selector); vm.expectRevert(IAnchorStateRegistry.AnchorStateRegistry_ImproperAnchorGame.selector);
anchorStateRegistry.setAnchorState(gameProxy); anchorStateRegistry.setAnchorState(gameProxy);
// Confirm that the anchor state has not updated. // Confirm that the anchor state has not updated.
...@@ -156,16 +690,90 @@ contract AnchorStateRegistry_TryUpdateAnchorState_Test is AnchorStateRegistry_In ...@@ -156,16 +690,90 @@ contract AnchorStateRegistry_TryUpdateAnchorState_Test is AnchorStateRegistry_In
assertEq(updatedRoot.raw(), root.raw()); assertEq(updatedRoot.raw(), root.raw());
} }
/// @dev Tests that setting the anchor state fails if the challenger wins. /// @notice Tests that setAnchorState will revert if the game is valid and the game status is
function test_setAnchorState_challengerWins_fails() public { /// CHALLENGER_WINS.
(Hash root, uint256 l2BlockNumber) = anchorStateRegistry.anchors(gameProxy.gameType()); /// @param _l2BlockNumber The L2 block number to use for the game.
function testFuzz_setAnchorState_challengerWins_fails(uint256 _l2BlockNumber) public {
// Grab block number of the existing anchor root.
(Hash root, uint256 l2BlockNumber) = anchorStateRegistry.getAnchorRoot();
// Mock the state that we want. // Mock the l2BlockNumber call.
vm.mockCall(address(gameProxy), abi.encodeCall(gameProxy.l2BlockNumber, ()), abi.encode(_l2BlockNumber));
// Mock the CHALLENGER_WINS state.
vm.mockCall(address(gameProxy), abi.encodeCall(gameProxy.status, ()), abi.encode(GameStatus.CHALLENGER_WINS)); vm.mockCall(address(gameProxy), abi.encodeCall(gameProxy.status, ()), abi.encode(GameStatus.CHALLENGER_WINS));
// Set the anchor state. // Make our game type the respected game type.
vm.mockCall(
address(optimismPortal2),
abi.encodeCall(optimismPortal2.respectedGameType, ()),
abi.encode(gameProxy.gameType())
);
// Try to update the anchor state.
vm.prank(superchainConfig.guardian()); vm.prank(superchainConfig.guardian());
vm.expectRevert(InvalidGameStatus.selector); vm.expectRevert(IAnchorStateRegistry.AnchorStateRegistry_InvalidAnchorGame.selector);
anchorStateRegistry.setAnchorState(gameProxy);
// Confirm that the anchor state has not updated.
(Hash updatedRoot, uint256 updatedL2BlockNumber) = anchorStateRegistry.getAnchorRoot();
assertEq(updatedL2BlockNumber, l2BlockNumber);
assertEq(updatedRoot.raw(), root.raw());
}
/// @notice Tests that setAnchorState will revert if the game is valid and the game status is
/// IN_PROGRESS.
/// @param _l2BlockNumber The L2 block number to use for the game.
function testFuzz_setAnchorState_inProgress_fails(uint256 _l2BlockNumber) public {
// Grab block number of the existing anchor root.
(Hash root, uint256 l2BlockNumber) = anchorStateRegistry.getAnchorRoot();
// Bound the new block number.
_l2BlockNumber = bound(_l2BlockNumber, l2BlockNumber, type(uint256).max);
// Mock the l2BlockNumber call.
vm.mockCall(address(gameProxy), abi.encodeCall(gameProxy.l2BlockNumber, ()), abi.encode(_l2BlockNumber));
// Mock the IN_PROGRESS state.
vm.mockCall(address(gameProxy), abi.encodeCall(gameProxy.status, ()), abi.encode(GameStatus.IN_PROGRESS));
// Make our game type the respected game type.
vm.mockCall(
address(optimismPortal2),
abi.encodeCall(optimismPortal2.respectedGameType, ()),
abi.encode(gameProxy.gameType())
);
// Try to update the anchor state.
vm.prank(superchainConfig.guardian());
vm.expectRevert(IAnchorStateRegistry.AnchorStateRegistry_InvalidAnchorGame.selector);
anchorStateRegistry.setAnchorState(gameProxy);
// Confirm that the anchor state has not updated.
(Hash updatedRoot, uint256 updatedL2BlockNumber) = anchorStateRegistry.getAnchorRoot();
assertEq(updatedL2BlockNumber, l2BlockNumber);
assertEq(updatedRoot.raw(), root.raw());
}
/// @notice Tests that setAnchorState will revert if the game is valid and the game type is not
/// the respected game type.
/// @param _l2BlockNumber The L2 block number to use for the game.
function testFuzz_setAnchorState_notRespectedGameType_fails(uint256 _l2BlockNumber) public {
// Grab block number of the existing anchor root.
(Hash root, uint256 l2BlockNumber) = anchorStateRegistry.anchors(gameProxy.gameType());
// Mock the l2BlockNumber call.
vm.mockCall(address(gameProxy), abi.encodeCall(gameProxy.l2BlockNumber, ()), abi.encode(_l2BlockNumber));
// Mock the DEFENDER_WINS state.
vm.mockCall(address(gameProxy), abi.encodeCall(gameProxy.status, ()), abi.encode(GameStatus.DEFENDER_WINS));
// Mock the respectedGameType call so that it does NOT match our game type.
vm.mockCall(address(optimismPortal2), abi.encodeCall(optimismPortal2.respectedGameType, ()), abi.encode(999));
// Try to update the anchor state.
vm.prank(superchainConfig.guardian());
vm.expectRevert(IAnchorStateRegistry.AnchorStateRegistry_ImproperAnchorGame.selector);
anchorStateRegistry.setAnchorState(gameProxy); anchorStateRegistry.setAnchorState(gameProxy);
// Confirm that the anchor state has not updated. // Confirm that the anchor state has not updated.
...@@ -174,16 +782,35 @@ contract AnchorStateRegistry_TryUpdateAnchorState_Test is AnchorStateRegistry_In ...@@ -174,16 +782,35 @@ contract AnchorStateRegistry_TryUpdateAnchorState_Test is AnchorStateRegistry_In
assertEq(updatedRoot.raw(), root.raw()); assertEq(updatedRoot.raw(), root.raw());
} }
/// @dev Tests that setting the anchor state fails if the game is in progress. /// @notice Tests that setAnchorState will revert if the game is valid and the game is blacklisted.
function test_setAnchorState_gameInProgress_fails() public { /// @param _l2BlockNumber The L2 block number to use for the game.
function test_setAnchorState_blacklistedGame_fails(uint256 _l2BlockNumber) public {
// Grab block number of the existing anchor root.
(Hash root, uint256 l2BlockNumber) = anchorStateRegistry.anchors(gameProxy.gameType()); (Hash root, uint256 l2BlockNumber) = anchorStateRegistry.anchors(gameProxy.gameType());
// Mock the state that we want. // Mock the l2BlockNumber call.
vm.mockCall(address(gameProxy), abi.encodeCall(gameProxy.status, ()), abi.encode(GameStatus.IN_PROGRESS)); vm.mockCall(address(gameProxy), abi.encodeCall(gameProxy.l2BlockNumber, ()), abi.encode(_l2BlockNumber));
// Mock the DEFENDER_WINS state.
vm.mockCall(address(gameProxy), abi.encodeCall(gameProxy.status, ()), abi.encode(GameStatus.DEFENDER_WINS));
// Make our game type the respected game type.
vm.mockCall(
address(optimismPortal2),
abi.encodeCall(optimismPortal2.respectedGameType, ()),
abi.encode(gameProxy.gameType())
);
// Mock the disputeGameBlacklist call to return true.
vm.mockCall(
address(optimismPortal2),
abi.encodeCall(optimismPortal2.disputeGameBlacklist, (gameProxy)),
abi.encode(true)
);
// Set the anchor state. // Set the anchor state.
vm.prank(superchainConfig.guardian()); vm.prank(superchainConfig.guardian());
vm.expectRevert(InvalidGameStatus.selector); vm.expectRevert(IAnchorStateRegistry.AnchorStateRegistry_ImproperAnchorGame.selector);
anchorStateRegistry.setAnchorState(gameProxy); anchorStateRegistry.setAnchorState(gameProxy);
// Confirm that the anchor state has not updated. // Confirm that the anchor state has not updated.
...@@ -192,18 +819,40 @@ contract AnchorStateRegistry_TryUpdateAnchorState_Test is AnchorStateRegistry_In ...@@ -192,18 +819,40 @@ contract AnchorStateRegistry_TryUpdateAnchorState_Test is AnchorStateRegistry_In
assertEq(updatedRoot.raw(), root.raw()); assertEq(updatedRoot.raw(), root.raw());
} }
/// @dev Tests that setting the anchor state succeeds. /// @notice Tests that setAnchorState will revert if the game is valid and the game is retired.
function test_setAnchorState_succeeds() public { /// @param _l2BlockNumber The L2 block number to use for the game.
// Mock the state that we want. function test_setAnchorState_retiredGame_fails(uint256 _l2BlockNumber) public {
// Grab block number of the existing anchor root.
(Hash root, uint256 l2BlockNumber) = anchorStateRegistry.anchors(gameProxy.gameType());
// Mock the l2BlockNumber call.
vm.mockCall(address(gameProxy), abi.encodeCall(gameProxy.l2BlockNumber, ()), abi.encode(_l2BlockNumber));
// Mock the DEFENDER_WINS state.
vm.mockCall(address(gameProxy), abi.encodeCall(gameProxy.status, ()), abi.encode(GameStatus.DEFENDER_WINS)); vm.mockCall(address(gameProxy), abi.encodeCall(gameProxy.status, ()), abi.encode(GameStatus.DEFENDER_WINS));
// Make our game type the respected game type.
vm.mockCall(
address(optimismPortal2),
abi.encodeCall(optimismPortal2.respectedGameType, ()),
abi.encode(gameProxy.gameType())
);
// Mock the respectedGameTypeUpdatedAt call to be later than the game's creation time.
vm.mockCall(
address(optimismPortal2),
abi.encodeCall(optimismPortal2.respectedGameTypeUpdatedAt, ()),
abi.encode(gameProxy.createdAt().raw() + 1)
);
// Set the anchor state. // Set the anchor state.
vm.prank(superchainConfig.guardian()); vm.prank(superchainConfig.guardian());
vm.expectRevert(IAnchorStateRegistry.AnchorStateRegistry_ImproperAnchorGame.selector);
anchorStateRegistry.setAnchorState(gameProxy); anchorStateRegistry.setAnchorState(gameProxy);
// Confirm that the anchor state has updated. // Confirm that the anchor state has not updated.
(Hash updatedRoot, uint256 updatedL2BlockNumber) = anchorStateRegistry.anchors(gameProxy.gameType()); (Hash updatedRoot, uint256 updatedL2BlockNumber) = anchorStateRegistry.anchors(gameProxy.gameType());
assertEq(updatedL2BlockNumber, gameProxy.l2BlockNumber()); assertEq(updatedL2BlockNumber, l2BlockNumber);
assertEq(updatedRoot.raw(), gameProxy.rootClaim().raw()); assertEq(updatedRoot.raw(), root.raw());
} }
} }
...@@ -8,6 +8,7 @@ import { IDelayedWETH } from "interfaces/dispute/IDelayedWETH.sol"; ...@@ -8,6 +8,7 @@ import { IDelayedWETH } from "interfaces/dispute/IDelayedWETH.sol";
import { IPreimageOracle } from "interfaces/cannon/IPreimageOracle.sol"; import { IPreimageOracle } from "interfaces/cannon/IPreimageOracle.sol";
import { IMIPS } from "interfaces/cannon/IMIPS.sol"; import { IMIPS } from "interfaces/cannon/IMIPS.sol";
import { IDisputeGameFactory } from "interfaces/dispute/IDisputeGameFactory.sol"; import { IDisputeGameFactory } from "interfaces/dispute/IDisputeGameFactory.sol";
import { IAnchorStateRegistry } from "interfaces/dispute/IAnchorStateRegistry.sol";
import { ISuperchainConfig } from "interfaces/L1/ISuperchainConfig.sol"; import { ISuperchainConfig } from "interfaces/L1/ISuperchainConfig.sol";
import { IProtocolVersions } from "interfaces/L1/IProtocolVersions.sol"; import { IProtocolVersions } from "interfaces/L1/IProtocolVersions.sol";
...@@ -92,6 +93,7 @@ contract DeployImplementationsOutput_Test is Test { ...@@ -92,6 +93,7 @@ contract DeployImplementationsOutput_Test is Test {
IOptimismMintableERC20Factory optimismMintableERC20FactoryImpl = IOptimismMintableERC20Factory optimismMintableERC20FactoryImpl =
IOptimismMintableERC20Factory(makeAddr("optimismMintableERC20FactoryImpl")); IOptimismMintableERC20Factory(makeAddr("optimismMintableERC20FactoryImpl"));
IDisputeGameFactory disputeGameFactoryImpl = IDisputeGameFactory(makeAddr("disputeGameFactoryImpl")); IDisputeGameFactory disputeGameFactoryImpl = IDisputeGameFactory(makeAddr("disputeGameFactoryImpl"));
IAnchorStateRegistry anchorStateRegistryImpl = IAnchorStateRegistry(makeAddr("anchorStateRegistryImpl"));
vm.etch(address(opcm), hex"01"); vm.etch(address(opcm), hex"01");
vm.etch(address(optimismPortalImpl), hex"01"); vm.etch(address(optimismPortalImpl), hex"01");
...@@ -104,6 +106,7 @@ contract DeployImplementationsOutput_Test is Test { ...@@ -104,6 +106,7 @@ contract DeployImplementationsOutput_Test is Test {
vm.etch(address(l1StandardBridgeImpl), hex"01"); vm.etch(address(l1StandardBridgeImpl), hex"01");
vm.etch(address(optimismMintableERC20FactoryImpl), hex"01"); vm.etch(address(optimismMintableERC20FactoryImpl), hex"01");
vm.etch(address(disputeGameFactoryImpl), hex"01"); vm.etch(address(disputeGameFactoryImpl), hex"01");
vm.etch(address(anchorStateRegistryImpl), hex"01");
dio.set(dio.opcm.selector, address(opcm)); dio.set(dio.opcm.selector, address(opcm));
dio.set(dio.optimismPortalImpl.selector, address(optimismPortalImpl)); dio.set(dio.optimismPortalImpl.selector, address(optimismPortalImpl));
dio.set(dio.delayedWETHImpl.selector, address(delayedWETHImpl)); dio.set(dio.delayedWETHImpl.selector, address(delayedWETHImpl));
...@@ -115,6 +118,7 @@ contract DeployImplementationsOutput_Test is Test { ...@@ -115,6 +118,7 @@ contract DeployImplementationsOutput_Test is Test {
dio.set(dio.l1StandardBridgeImpl.selector, address(l1StandardBridgeImpl)); dio.set(dio.l1StandardBridgeImpl.selector, address(l1StandardBridgeImpl));
dio.set(dio.optimismMintableERC20FactoryImpl.selector, address(optimismMintableERC20FactoryImpl)); dio.set(dio.optimismMintableERC20FactoryImpl.selector, address(optimismMintableERC20FactoryImpl));
dio.set(dio.disputeGameFactoryImpl.selector, address(disputeGameFactoryImpl)); dio.set(dio.disputeGameFactoryImpl.selector, address(disputeGameFactoryImpl));
dio.set(dio.anchorStateRegistryImpl.selector, address(anchorStateRegistryImpl));
assertEq(address(opcm), address(dio.opcm()), "50"); assertEq(address(opcm), address(dio.opcm()), "50");
assertEq(address(optimismPortalImpl), address(dio.optimismPortalImpl()), "100"); assertEq(address(optimismPortalImpl), address(dio.optimismPortalImpl()), "100");
...@@ -127,6 +131,7 @@ contract DeployImplementationsOutput_Test is Test { ...@@ -127,6 +131,7 @@ contract DeployImplementationsOutput_Test is Test {
assertEq(address(l1StandardBridgeImpl), address(dio.l1StandardBridgeImpl()), "800"); assertEq(address(l1StandardBridgeImpl), address(dio.l1StandardBridgeImpl()), "800");
assertEq(address(optimismMintableERC20FactoryImpl), address(dio.optimismMintableERC20FactoryImpl()), "900"); assertEq(address(optimismMintableERC20FactoryImpl), address(dio.optimismMintableERC20FactoryImpl()), "900");
assertEq(address(disputeGameFactoryImpl), address(dio.disputeGameFactoryImpl()), "950"); assertEq(address(disputeGameFactoryImpl), address(dio.disputeGameFactoryImpl()), "950");
assertEq(address(anchorStateRegistryImpl), address(dio.anchorStateRegistryImpl()), "960");
} }
function test_getters_whenNotSet_reverts() public { function test_getters_whenNotSet_reverts() public {
...@@ -161,6 +166,9 @@ contract DeployImplementationsOutput_Test is Test { ...@@ -161,6 +166,9 @@ contract DeployImplementationsOutput_Test is Test {
vm.expectRevert(expectedErr); vm.expectRevert(expectedErr);
dio.disputeGameFactoryImpl(); dio.disputeGameFactoryImpl();
vm.expectRevert(expectedErr);
dio.anchorStateRegistryImpl();
} }
function test_getters_whenAddrHasNoCode_reverts() public { function test_getters_whenAddrHasNoCode_reverts() public {
...@@ -267,6 +275,7 @@ contract DeployImplementations_Test is Test { ...@@ -267,6 +275,7 @@ contract DeployImplementations_Test is Test {
deployImplementations.deployPreimageOracleSingleton(dii, dio); deployImplementations.deployPreimageOracleSingleton(dii, dio);
deployImplementations.deployMipsSingleton(dii, dio); deployImplementations.deployMipsSingleton(dii, dio);
deployImplementations.deployDisputeGameFactoryImpl(dio); deployImplementations.deployDisputeGameFactoryImpl(dio);
deployImplementations.deployAnchorStateRegistryImpl(dio);
deployImplementations.deployOPContractsManager(dii, dio); deployImplementations.deployOPContractsManager(dii, dio);
// Store the original addresses. // Store the original addresses.
...@@ -280,6 +289,7 @@ contract DeployImplementations_Test is Test { ...@@ -280,6 +289,7 @@ contract DeployImplementations_Test is Test {
address preimageOracleSingleton = address(dio.preimageOracleSingleton()); address preimageOracleSingleton = address(dio.preimageOracleSingleton());
address mipsSingleton = address(dio.mipsSingleton()); address mipsSingleton = address(dio.mipsSingleton());
address disputeGameFactoryImpl = address(dio.disputeGameFactoryImpl()); address disputeGameFactoryImpl = address(dio.disputeGameFactoryImpl());
address anchorStateRegistryImpl = address(dio.anchorStateRegistryImpl());
address opcm = address(dio.opcm()); address opcm = address(dio.opcm());
// Do the deployments again. Thi should be a noop. // Do the deployments again. Thi should be a noop.
...@@ -293,6 +303,7 @@ contract DeployImplementations_Test is Test { ...@@ -293,6 +303,7 @@ contract DeployImplementations_Test is Test {
deployImplementations.deployPreimageOracleSingleton(dii, dio); deployImplementations.deployPreimageOracleSingleton(dii, dio);
deployImplementations.deployMipsSingleton(dii, dio); deployImplementations.deployMipsSingleton(dii, dio);
deployImplementations.deployDisputeGameFactoryImpl(dio); deployImplementations.deployDisputeGameFactoryImpl(dio);
deployImplementations.deployAnchorStateRegistryImpl(dio);
deployImplementations.deployOPContractsManager(dii, dio); deployImplementations.deployOPContractsManager(dii, dio);
// Assert that the addresses did not change. // Assert that the addresses did not change.
...@@ -306,7 +317,8 @@ contract DeployImplementations_Test is Test { ...@@ -306,7 +317,8 @@ contract DeployImplementations_Test is Test {
assertEq(preimageOracleSingleton, address(dio.preimageOracleSingleton()), "800"); assertEq(preimageOracleSingleton, address(dio.preimageOracleSingleton()), "800");
assertEq(mipsSingleton, address(dio.mipsSingleton()), "900"); assertEq(mipsSingleton, address(dio.mipsSingleton()), "900");
assertEq(disputeGameFactoryImpl, address(dio.disputeGameFactoryImpl()), "1000"); assertEq(disputeGameFactoryImpl, address(dio.disputeGameFactoryImpl()), "1000");
assertEq(opcm, address(dio.opcm()), "1100"); assertEq(anchorStateRegistryImpl, address(dio.anchorStateRegistryImpl()), "1100");
assertEq(opcm, address(dio.opcm()), "1200");
} }
function testFuzz_run_memory_succeeds(bytes32 _seed) public { function testFuzz_run_memory_succeeds(bytes32 _seed) public {
......
...@@ -40,9 +40,6 @@ contract DeployOPCMInput_Test is Test { ...@@ -40,9 +40,6 @@ contract DeployOPCMInput_Test is Test {
vm.expectRevert("DeployOPCMInput: not set"); vm.expectRevert("DeployOPCMInput: not set");
dii.resolvedDelegateProxyBlueprint(); dii.resolvedDelegateProxyBlueprint();
vm.expectRevert("DeployOPCMInput: not set");
dii.anchorStateRegistryBlueprint();
vm.expectRevert("DeployOPCMInput: not set"); vm.expectRevert("DeployOPCMInput: not set");
dii.permissionedDisputeGame1Blueprint(); dii.permissionedDisputeGame1Blueprint();
...@@ -70,6 +67,9 @@ contract DeployOPCMInput_Test is Test { ...@@ -70,6 +67,9 @@ contract DeployOPCMInput_Test is Test {
vm.expectRevert("DeployOPCMInput: not set"); vm.expectRevert("DeployOPCMInput: not set");
dii.disputeGameFactoryImpl(); dii.disputeGameFactoryImpl();
vm.expectRevert("DeployOPCMInput: not set");
dii.anchorStateRegistryImpl();
vm.expectRevert("DeployOPCMInput: not set"); vm.expectRevert("DeployOPCMInput: not set");
dii.delayedWETHImpl(); dii.delayedWETHImpl();
...@@ -87,7 +87,6 @@ contract DeployOPCMInput_Test is Test { ...@@ -87,7 +87,6 @@ contract DeployOPCMInput_Test is Test {
address proxyAdminBlueprint = makeAddr("proxyAdminBlueprint"); address proxyAdminBlueprint = makeAddr("proxyAdminBlueprint");
address l1ChugSplashProxyBlueprint = makeAddr("l1ChugSplashProxyBlueprint"); address l1ChugSplashProxyBlueprint = makeAddr("l1ChugSplashProxyBlueprint");
address resolvedDelegateProxyBlueprint = makeAddr("resolvedDelegateProxyBlueprint"); address resolvedDelegateProxyBlueprint = makeAddr("resolvedDelegateProxyBlueprint");
address anchorStateRegistryBlueprint = makeAddr("anchorStateRegistryBlueprint");
address permissionedDisputeGame1Blueprint = makeAddr("permissionedDisputeGame1Blueprint"); address permissionedDisputeGame1Blueprint = makeAddr("permissionedDisputeGame1Blueprint");
address permissionedDisputeGame2Blueprint = makeAddr("permissionedDisputeGame2Blueprint"); address permissionedDisputeGame2Blueprint = makeAddr("permissionedDisputeGame2Blueprint");
...@@ -99,7 +98,6 @@ contract DeployOPCMInput_Test is Test { ...@@ -99,7 +98,6 @@ contract DeployOPCMInput_Test is Test {
dii.set(dii.proxyAdminBlueprint.selector, proxyAdminBlueprint); dii.set(dii.proxyAdminBlueprint.selector, proxyAdminBlueprint);
dii.set(dii.l1ChugSplashProxyBlueprint.selector, l1ChugSplashProxyBlueprint); dii.set(dii.l1ChugSplashProxyBlueprint.selector, l1ChugSplashProxyBlueprint);
dii.set(dii.resolvedDelegateProxyBlueprint.selector, resolvedDelegateProxyBlueprint); dii.set(dii.resolvedDelegateProxyBlueprint.selector, resolvedDelegateProxyBlueprint);
dii.set(dii.anchorStateRegistryBlueprint.selector, anchorStateRegistryBlueprint);
dii.set(dii.permissionedDisputeGame1Blueprint.selector, permissionedDisputeGame1Blueprint); dii.set(dii.permissionedDisputeGame1Blueprint.selector, permissionedDisputeGame1Blueprint);
dii.set(dii.permissionedDisputeGame2Blueprint.selector, permissionedDisputeGame2Blueprint); dii.set(dii.permissionedDisputeGame2Blueprint.selector, permissionedDisputeGame2Blueprint);
...@@ -111,7 +109,6 @@ contract DeployOPCMInput_Test is Test { ...@@ -111,7 +109,6 @@ contract DeployOPCMInput_Test is Test {
assertEq(dii.proxyAdminBlueprint(), proxyAdminBlueprint, "300"); assertEq(dii.proxyAdminBlueprint(), proxyAdminBlueprint, "300");
assertEq(dii.l1ChugSplashProxyBlueprint(), l1ChugSplashProxyBlueprint, "350"); assertEq(dii.l1ChugSplashProxyBlueprint(), l1ChugSplashProxyBlueprint, "350");
assertEq(dii.resolvedDelegateProxyBlueprint(), resolvedDelegateProxyBlueprint, "400"); assertEq(dii.resolvedDelegateProxyBlueprint(), resolvedDelegateProxyBlueprint, "400");
assertEq(dii.anchorStateRegistryBlueprint(), anchorStateRegistryBlueprint, "450");
assertEq(dii.permissionedDisputeGame1Blueprint(), permissionedDisputeGame1Blueprint, "500"); assertEq(dii.permissionedDisputeGame1Blueprint(), permissionedDisputeGame1Blueprint, "500");
assertEq(dii.permissionedDisputeGame2Blueprint(), permissionedDisputeGame2Blueprint, "550"); assertEq(dii.permissionedDisputeGame2Blueprint(), permissionedDisputeGame2Blueprint, "550");
} }
...@@ -124,6 +121,7 @@ contract DeployOPCMInput_Test is Test { ...@@ -124,6 +121,7 @@ contract DeployOPCMInput_Test is Test {
address l1CrossDomainMessengerImpl = makeAddr("l1CrossDomainMessengerImpl"); address l1CrossDomainMessengerImpl = makeAddr("l1CrossDomainMessengerImpl");
address l1StandardBridgeImpl = makeAddr("l1StandardBridgeImpl"); address l1StandardBridgeImpl = makeAddr("l1StandardBridgeImpl");
address disputeGameFactoryImpl = makeAddr("disputeGameFactoryImpl"); address disputeGameFactoryImpl = makeAddr("disputeGameFactoryImpl");
address anchorStateRegistryImpl = makeAddr("anchorStateRegistryImpl");
address delayedWETHImpl = makeAddr("delayedWETHImpl"); address delayedWETHImpl = makeAddr("delayedWETHImpl");
address mipsImpl = makeAddr("mipsImpl"); address mipsImpl = makeAddr("mipsImpl");
...@@ -134,6 +132,7 @@ contract DeployOPCMInput_Test is Test { ...@@ -134,6 +132,7 @@ contract DeployOPCMInput_Test is Test {
dii.set(dii.l1CrossDomainMessengerImpl.selector, l1CrossDomainMessengerImpl); dii.set(dii.l1CrossDomainMessengerImpl.selector, l1CrossDomainMessengerImpl);
dii.set(dii.l1StandardBridgeImpl.selector, l1StandardBridgeImpl); dii.set(dii.l1StandardBridgeImpl.selector, l1StandardBridgeImpl);
dii.set(dii.disputeGameFactoryImpl.selector, disputeGameFactoryImpl); dii.set(dii.disputeGameFactoryImpl.selector, disputeGameFactoryImpl);
dii.set(dii.anchorStateRegistryImpl.selector, anchorStateRegistryImpl);
dii.set(dii.delayedWETHImpl.selector, delayedWETHImpl); dii.set(dii.delayedWETHImpl.selector, delayedWETHImpl);
dii.set(dii.mipsImpl.selector, mipsImpl); dii.set(dii.mipsImpl.selector, mipsImpl);
...@@ -225,7 +224,6 @@ contract DeployOPCMTest is Test { ...@@ -225,7 +224,6 @@ contract DeployOPCMTest is Test {
doi.set(doi.proxyAdminBlueprint.selector, makeAddr("proxyAdminBlueprint")); doi.set(doi.proxyAdminBlueprint.selector, makeAddr("proxyAdminBlueprint"));
doi.set(doi.l1ChugSplashProxyBlueprint.selector, makeAddr("l1ChugSplashProxyBlueprint")); doi.set(doi.l1ChugSplashProxyBlueprint.selector, makeAddr("l1ChugSplashProxyBlueprint"));
doi.set(doi.resolvedDelegateProxyBlueprint.selector, makeAddr("resolvedDelegateProxyBlueprint")); doi.set(doi.resolvedDelegateProxyBlueprint.selector, makeAddr("resolvedDelegateProxyBlueprint"));
doi.set(doi.anchorStateRegistryBlueprint.selector, makeAddr("anchorStateRegistryBlueprint"));
doi.set(doi.permissionedDisputeGame1Blueprint.selector, makeAddr("permissionedDisputeGame1Blueprint")); doi.set(doi.permissionedDisputeGame1Blueprint.selector, makeAddr("permissionedDisputeGame1Blueprint"));
doi.set(doi.permissionedDisputeGame2Blueprint.selector, makeAddr("permissionedDisputeGame2Blueprint")); doi.set(doi.permissionedDisputeGame2Blueprint.selector, makeAddr("permissionedDisputeGame2Blueprint"));
...@@ -237,6 +235,7 @@ contract DeployOPCMTest is Test { ...@@ -237,6 +235,7 @@ contract DeployOPCMTest is Test {
doi.set(doi.l1CrossDomainMessengerImpl.selector, makeAddr("l1CrossDomainMessengerImpl")); doi.set(doi.l1CrossDomainMessengerImpl.selector, makeAddr("l1CrossDomainMessengerImpl"));
doi.set(doi.l1StandardBridgeImpl.selector, makeAddr("l1StandardBridgeImpl")); doi.set(doi.l1StandardBridgeImpl.selector, makeAddr("l1StandardBridgeImpl"));
doi.set(doi.disputeGameFactoryImpl.selector, makeAddr("disputeGameFactoryImpl")); doi.set(doi.disputeGameFactoryImpl.selector, makeAddr("disputeGameFactoryImpl"));
doi.set(doi.anchorStateRegistryImpl.selector, makeAddr("anchorStateRegistryImpl"));
doi.set(doi.delayedWETHImpl.selector, makeAddr("delayedWETHImpl")); doi.set(doi.delayedWETHImpl.selector, makeAddr("delayedWETHImpl"));
doi.set(doi.mipsImpl.selector, makeAddr("mipsImpl")); doi.set(doi.mipsImpl.selector, makeAddr("mipsImpl"));
...@@ -249,7 +248,6 @@ contract DeployOPCMTest is Test { ...@@ -249,7 +248,6 @@ contract DeployOPCMTest is Test {
vm.etch(doi.proxyAdminBlueprint(), hex"01"); vm.etch(doi.proxyAdminBlueprint(), hex"01");
vm.etch(doi.l1ChugSplashProxyBlueprint(), hex"01"); vm.etch(doi.l1ChugSplashProxyBlueprint(), hex"01");
vm.etch(doi.resolvedDelegateProxyBlueprint(), hex"01"); vm.etch(doi.resolvedDelegateProxyBlueprint(), hex"01");
vm.etch(doi.anchorStateRegistryBlueprint(), hex"01");
vm.etch(doi.permissionedDisputeGame1Blueprint(), hex"01"); vm.etch(doi.permissionedDisputeGame1Blueprint(), hex"01");
vm.etch(doi.permissionedDisputeGame2Blueprint(), hex"01"); vm.etch(doi.permissionedDisputeGame2Blueprint(), hex"01");
......
...@@ -155,7 +155,6 @@ contract DeployOPChainOutput_Test is Test { ...@@ -155,7 +155,6 @@ contract DeployOPChainOutput_Test is Test {
doo.set(doo.optimismPortalProxy.selector, address(optimismPortalProxy)); doo.set(doo.optimismPortalProxy.selector, address(optimismPortalProxy));
doo.set(doo.disputeGameFactoryProxy.selector, address(disputeGameFactoryProxy)); doo.set(doo.disputeGameFactoryProxy.selector, address(disputeGameFactoryProxy));
doo.set(doo.anchorStateRegistryProxy.selector, address(anchorStateRegistryProxy)); doo.set(doo.anchorStateRegistryProxy.selector, address(anchorStateRegistryProxy));
doo.set(doo.anchorStateRegistryImpl.selector, address(anchorStateRegistryImpl));
doo.set(doo.faultDisputeGame.selector, address(faultDisputeGame)); doo.set(doo.faultDisputeGame.selector, address(faultDisputeGame));
doo.set(doo.permissionedDisputeGame.selector, address(permissionedDisputeGame)); doo.set(doo.permissionedDisputeGame.selector, address(permissionedDisputeGame));
doo.set(doo.delayedWETHPermissionedGameProxy.selector, address(delayedWETHPermissionedGameProxy)); doo.set(doo.delayedWETHPermissionedGameProxy.selector, address(delayedWETHPermissionedGameProxy));
...@@ -172,7 +171,6 @@ contract DeployOPChainOutput_Test is Test { ...@@ -172,7 +171,6 @@ contract DeployOPChainOutput_Test is Test {
assertEq(address(optimismPortalProxy), address(doo.optimismPortalProxy()), "800"); assertEq(address(optimismPortalProxy), address(doo.optimismPortalProxy()), "800");
assertEq(address(disputeGameFactoryProxy), address(doo.disputeGameFactoryProxy()), "900"); assertEq(address(disputeGameFactoryProxy), address(doo.disputeGameFactoryProxy()), "900");
assertEq(address(anchorStateRegistryProxy), address(doo.anchorStateRegistryProxy()), "1100"); assertEq(address(anchorStateRegistryProxy), address(doo.anchorStateRegistryProxy()), "1100");
assertEq(address(anchorStateRegistryImpl), address(doo.anchorStateRegistryImpl()), "1200");
assertEq(address(faultDisputeGame), address(doo.faultDisputeGame()), "1300"); assertEq(address(faultDisputeGame), address(doo.faultDisputeGame()), "1300");
assertEq(address(permissionedDisputeGame), address(doo.permissionedDisputeGame()), "1400"); assertEq(address(permissionedDisputeGame), address(doo.permissionedDisputeGame()), "1400");
assertEq(address(delayedWETHPermissionedGameProxy), address(doo.delayedWETHPermissionedGameProxy()), "1500"); assertEq(address(delayedWETHPermissionedGameProxy), address(doo.delayedWETHPermissionedGameProxy()), "1500");
...@@ -214,9 +212,6 @@ contract DeployOPChainOutput_Test is Test { ...@@ -214,9 +212,6 @@ contract DeployOPChainOutput_Test is Test {
vm.expectRevert(expectedErr); vm.expectRevert(expectedErr);
doo.anchorStateRegistryProxy(); doo.anchorStateRegistryProxy();
vm.expectRevert(expectedErr);
doo.anchorStateRegistryImpl();
vm.expectRevert(expectedErr); vm.expectRevert(expectedErr);
doo.faultDisputeGame(); doo.faultDisputeGame();
...@@ -275,10 +270,6 @@ contract DeployOPChainOutput_Test is Test { ...@@ -275,10 +270,6 @@ contract DeployOPChainOutput_Test is Test {
vm.expectRevert(expectedErr); vm.expectRevert(expectedErr);
doo.anchorStateRegistryProxy(); doo.anchorStateRegistryProxy();
doo.set(doo.anchorStateRegistryImpl.selector, emptyAddr);
vm.expectRevert(expectedErr);
doo.anchorStateRegistryImpl();
doo.set(doo.faultDisputeGame.selector, emptyAddr); doo.set(doo.faultDisputeGame.selector, emptyAddr);
vm.expectRevert(expectedErr); vm.expectRevert(expectedErr);
doo.faultDisputeGame(); doo.faultDisputeGame();
...@@ -336,7 +327,7 @@ contract DeployOPChain_TestBase is Test { ...@@ -336,7 +327,7 @@ contract DeployOPChain_TestBase is Test {
uint32 basefeeScalar = 100; uint32 basefeeScalar = 100;
uint32 blobBaseFeeScalar = 200; uint32 blobBaseFeeScalar = 200;
uint256 l2ChainId = 300; uint256 l2ChainId = 300;
IAnchorStateRegistry.StartingAnchorRoot[] startingAnchorRoots; OutputRoot startingAnchorRoot = OutputRoot({ root: Hash.wrap(keccak256("defaultOutputRoot")), l2BlockNumber: 400 });
OPContractsManager opcm = OPContractsManager(address(0)); OPContractsManager opcm = OPContractsManager(address(0));
string saltMixer = "defaultSaltMixer"; string saltMixer = "defaultSaltMixer";
uint64 gasLimit = 60_000_000; uint64 gasLimit = 60_000_000;
...@@ -349,25 +340,6 @@ contract DeployOPChain_TestBase is Test { ...@@ -349,25 +340,6 @@ contract DeployOPChain_TestBase is Test {
uint64 disputeMaxClockDuration = Duration.unwrap(Duration.wrap(3.5 days)); uint64 disputeMaxClockDuration = Duration.unwrap(Duration.wrap(3.5 days));
function setUp() public virtual { function setUp() public virtual {
// Set defaults for reference types
uint256 cannonBlock = 400;
uint256 permissionedBlock = 500;
startingAnchorRoots.push(
IAnchorStateRegistry.StartingAnchorRoot({
gameType: GameTypes.CANNON,
outputRoot: OutputRoot({ root: Hash.wrap(keccak256("defaultOutputRootCannon")), l2BlockNumber: cannonBlock })
})
);
startingAnchorRoots.push(
IAnchorStateRegistry.StartingAnchorRoot({
gameType: GameTypes.PERMISSIONED_CANNON,
outputRoot: OutputRoot({
root: Hash.wrap(keccak256("defaultOutputRootPermissioned")),
l2BlockNumber: permissionedBlock
})
})
);
// Configure and deploy Superchain contracts // Configure and deploy Superchain contracts
DeploySuperchain deploySuperchain = new DeploySuperchain(); DeploySuperchain deploySuperchain = new DeploySuperchain();
(DeploySuperchainInput dsi, DeploySuperchainOutput dso) = deploySuperchain.etchIOContracts(); (DeploySuperchainInput dsi, DeploySuperchainOutput dso) = deploySuperchain.etchIOContracts();
...@@ -432,25 +404,6 @@ contract DeployOPChain_Test is DeployOPChain_TestBase { ...@@ -432,25 +404,6 @@ contract DeployOPChain_Test is DeployOPChain_TestBase {
blobBaseFeeScalar = uint32(uint256(hash(_seed, 7))); blobBaseFeeScalar = uint32(uint256(hash(_seed, 7)));
l2ChainId = uint256(hash(_seed, 8)); l2ChainId = uint256(hash(_seed, 8));
// Set the initial anchor states. The typical usage we expect is to pass in one root per game type.
uint256 cannonBlock = uint256(hash(_seed, 9));
uint256 permissionedBlock = uint256(hash(_seed, 10));
startingAnchorRoots.push(
IAnchorStateRegistry.StartingAnchorRoot({
gameType: GameTypes.CANNON,
outputRoot: OutputRoot({ root: Hash.wrap(keccak256(abi.encode(_seed, 11))), l2BlockNumber: cannonBlock })
})
);
startingAnchorRoots.push(
IAnchorStateRegistry.StartingAnchorRoot({
gameType: GameTypes.PERMISSIONED_CANNON,
outputRoot: OutputRoot({
root: Hash.wrap(keccak256(abi.encode(_seed, 12))),
l2BlockNumber: permissionedBlock
})
})
);
doi.set(doi.opChainProxyAdminOwner.selector, opChainProxyAdminOwner); doi.set(doi.opChainProxyAdminOwner.selector, opChainProxyAdminOwner);
doi.set(doi.systemConfigOwner.selector, systemConfigOwner); doi.set(doi.systemConfigOwner.selector, systemConfigOwner);
doi.set(doi.batcher.selector, batcher); doi.set(doi.batcher.selector, batcher);
......
...@@ -554,11 +554,19 @@ contract Specification_Test is CommonTest { ...@@ -554,11 +554,19 @@ contract Specification_Test is CommonTest {
// AnchorStateRegistry // AnchorStateRegistry
_addSpec({ _name: "AnchorStateRegistry", _sel: _getSel("anchors(uint32)") }); _addSpec({ _name: "AnchorStateRegistry", _sel: _getSel("anchors(uint32)") });
_addSpec({ _name: "AnchorStateRegistry", _sel: _getSel("getAnchorRoot()") });
_addSpec({ _name: "AnchorStateRegistry", _sel: _getSel("disputeGameFactory()") }); _addSpec({ _name: "AnchorStateRegistry", _sel: _getSel("disputeGameFactory()") });
_addSpec({ _name: "AnchorStateRegistry", _sel: _getSel("initialize((uint32,(bytes32,uint256))[],address)") }); _addSpec({ _name: "AnchorStateRegistry", _sel: _getSel("portal()") });
_addSpec({ _name: "AnchorStateRegistry", _sel: _getSel("anchorGame()") });
_addSpec({ _name: "AnchorStateRegistry", _sel: _getSel("initialize(address,address,address,(bytes32,uint256))") });
_addSpec({ _name: "AnchorStateRegistry", _sel: _getSel("tryUpdateAnchorState()") }); _addSpec({ _name: "AnchorStateRegistry", _sel: _getSel("tryUpdateAnchorState()") });
_addSpec({ _name: "AnchorStateRegistry", _sel: _getSel("setAnchorState(address)"), _auth: Role.GUARDIAN }); _addSpec({ _name: "AnchorStateRegistry", _sel: _getSel("setAnchorState(address)"), _auth: Role.GUARDIAN });
_addSpec({ _name: "AnchorStateRegistry", _sel: _getSel("version()") }); _addSpec({ _name: "AnchorStateRegistry", _sel: _getSel("version()") });
_addSpec({ _name: "AnchorStateRegistry", _sel: _getSel("isGameRegistered(address)") });
_addSpec({ _name: "AnchorStateRegistry", _sel: _getSel("isGameRespected(address)") });
_addSpec({ _name: "AnchorStateRegistry", _sel: _getSel("isGameBlacklisted(address)") });
_addSpec({ _name: "AnchorStateRegistry", _sel: _getSel("isGameRetired(address)") });
_addSpec({ _name: "AnchorStateRegistry", _sel: _getSel("isGameProper(address)") });
_addSpec({ _name: "AnchorStateRegistry", _sel: _getSel("superchainConfig()") }); _addSpec({ _name: "AnchorStateRegistry", _sel: _getSel("superchainConfig()") });
// PermissionedDisputeGame // PermissionedDisputeGame
......
...@@ -10,15 +10,16 @@ import { Process } from "scripts/libraries/Process.sol"; ...@@ -10,15 +10,16 @@ import { Process } from "scripts/libraries/Process.sol";
// Libraries // Libraries
import { LibString } from "@solady/utils/LibString.sol"; import { LibString } from "@solady/utils/LibString.sol";
import { GameType } from "src/dispute/lib/Types.sol"; import { GameType, Hash, OutputRoot } from "src/dispute/lib/Types.sol";
import { EIP1967Helper } from "test/mocks/EIP1967Helper.sol"; import { EIP1967Helper } from "test/mocks/EIP1967Helper.sol";
// Interfaces // Interfaces
import { ISystemConfig } from "interfaces/L1/ISystemConfig.sol"; import { ISystemConfig } from "interfaces/L1/ISystemConfig.sol";
import { IResourceMetering } from "interfaces/L1/IResourceMetering.sol"; import { IResourceMetering } from "interfaces/L1/IResourceMetering.sol";
import { ISuperchainConfig } from "interfaces/L1/ISuperchainConfig.sol"; import { ISuperchainConfig } from "interfaces/L1/ISuperchainConfig.sol";
import { IDisputeGameFactory } from "interfaces/dispute/IDisputeGameFactory.sol";
import { IOptimismPortal2 } from "interfaces/L1/IOptimismPortal2.sol";
import { ProtocolVersion } from "interfaces/L1/IProtocolVersions.sol"; import { ProtocolVersion } from "interfaces/L1/IProtocolVersions.sol";
import { IAnchorStateRegistry } from "interfaces/dispute/IAnchorStateRegistry.sol";
/// @title Initializer_Test /// @title Initializer_Test
/// @dev Ensures that the `initialize()` function on contracts cannot be called more than /// @dev Ensures that the `initialize()` function on contracts cannot be called more than
...@@ -318,7 +319,12 @@ contract Initializer_Test is CommonTest { ...@@ -318,7 +319,12 @@ contract Initializer_Test is CommonTest {
target: EIP1967Helper.getImplementation(address(anchorStateRegistry)), target: EIP1967Helper.getImplementation(address(anchorStateRegistry)),
initCalldata: abi.encodeCall( initCalldata: abi.encodeCall(
anchorStateRegistry.initialize, anchorStateRegistry.initialize,
(new IAnchorStateRegistry.StartingAnchorRoot[](1), ISuperchainConfig(address(0))) (
ISuperchainConfig(address(0)),
IDisputeGameFactory(address(0)),
IOptimismPortal2(payable(0)),
OutputRoot({ root: Hash.wrap(bytes32(0)), l2BlockNumber: 0 })
)
) )
}) })
); );
...@@ -329,7 +335,12 @@ contract Initializer_Test is CommonTest { ...@@ -329,7 +335,12 @@ contract Initializer_Test is CommonTest {
target: address(anchorStateRegistry), target: address(anchorStateRegistry),
initCalldata: abi.encodeCall( initCalldata: abi.encodeCall(
anchorStateRegistry.initialize, anchorStateRegistry.initialize,
(new IAnchorStateRegistry.StartingAnchorRoot[](1), ISuperchainConfig(address(0))) (
ISuperchainConfig(address(0)),
IDisputeGameFactory(address(0)),
IOptimismPortal2(payable(0)),
OutputRoot({ root: Hash.wrap(bytes32(0)), l2BlockNumber: 0 })
)
) )
}) })
); );
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment