diff --git a/go.mod b/go.mod index 49cc15545b64..0a38b5aea9c5 100644 --- a/go.mod +++ b/go.mod @@ -34,6 +34,7 @@ require ( github.com/libp2p/go-libp2p-mplex v0.9.0 github.com/libp2p/go-libp2p-pubsub v0.12.0 github.com/libp2p/go-libp2p-testing v0.12.0 + github.com/lmittmann/w3 v0.17.1 github.com/mattn/go-isatty v0.0.20 github.com/minio/minio-go/v7 v7.0.81 github.com/multiformats/go-base32 v0.1.0 @@ -157,7 +158,7 @@ require ( github.com/libp2p/go-yamux/v4 v4.0.1 // indirect github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd // indirect github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-runewidth v0.0.14 // indirect + github.com/mattn/go-runewidth v0.0.15 // indirect github.com/mholt/archiver v3.1.1+incompatible // indirect github.com/miekg/dns v1.1.62 // indirect github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b // indirect @@ -208,7 +209,7 @@ require ( github.com/quic-go/quic-go v0.46.0 // indirect github.com/quic-go/webtransport-go v0.8.0 // indirect github.com/raulk/go-watchdog v1.3.0 // indirect - github.com/rivo/uniseg v0.4.4 // indirect + github.com/rivo/uniseg v0.4.7 // indirect github.com/rogpeppe/go-internal v1.11.0 // indirect github.com/rs/cors v1.11.0 // indirect github.com/rs/xid v1.6.0 // indirect diff --git a/go.sum b/go.sum index 355a484d01aa..1338ec18afb7 100644 --- a/go.sum +++ b/go.sum @@ -485,6 +485,8 @@ github.com/libp2p/go-reuseport v0.4.0 h1:nR5KU7hD0WxXCJbmw7r2rhRYruNRl2koHw8fQsc github.com/libp2p/go-reuseport v0.4.0/go.mod h1:ZtI03j/wO5hZVDFo2jKywN6bYKWLOy8Se6DrI2E1cLU= github.com/libp2p/go-yamux/v4 v4.0.1 h1:FfDR4S1wj6Bw2Pqbc8Uz7pCxeRBPbwsBbEdfwiCypkQ= github.com/libp2p/go-yamux/v4 v4.0.1/go.mod h1:NWjl8ZTLOGlozrXSOZ/HlfG++39iKNnM5wwmtQP1YB4= +github.com/lmittmann/w3 v0.17.1 h1:zdXIimmNmYfqOFur+Jqc9Yqwtq6jwnsQufbTOnSAtW4= +github.com/lmittmann/w3 v0.17.1/go.mod h1:WVUGMbL83WYBu4Sge3SVlW3qIG4VaHe+S8+UUnwz9Eg= github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= @@ -509,8 +511,8 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= -github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= -github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= +github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/mholt/archiver v3.1.1+incompatible h1:1dCVxuqs0dJseYEhi5pl7MYPH9zDa1wBi7mF09cbNkU= github.com/mholt/archiver v3.1.1+incompatible/go.mod h1:Dh2dOXnSdiLxRiPoVfIr/fI1TwETms9B8CTWfeh7ROU= @@ -711,8 +713,8 @@ github.com/quic-go/webtransport-go v0.8.0/go.mod h1:N99tjprW432Ut5ONql/aUhSLT0YV github.com/raulk/go-watchdog v1.3.0 h1:oUmdlHxdkXRJlwfG0O9omj8ukerm8MEQavSiDTEtBsk= github.com/raulk/go-watchdog v1.3.0/go.mod h1:fIvOnLbF0b0ZwkB9YU4mOW9Did//4vPZtDqv66NfsMU= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= -github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= -github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= +github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= +github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= diff --git a/op-chain-ops/foundry/allocs.go b/op-chain-ops/foundry/allocs.go index af0c28f4faf0..715524a35d49 100644 --- a/op-chain-ops/foundry/allocs.go +++ b/op-chain-ops/foundry/allocs.go @@ -1,6 +1,7 @@ package foundry import ( + "bytes" "encoding/json" "fmt" "maps" @@ -76,9 +77,19 @@ func (d *forgeAllocsDump) OnAccount(address *common.Address, account state.DumpA } } +// Copy returns a deep copy of the ForgeAllocs. We can't +// perform a shallow copy here because some tests modify +// the accounts individually. func (d *ForgeAllocs) Copy() *ForgeAllocs { out := make(types.GenesisAlloc, len(d.Accounts)) - maps.Copy(out, d.Accounts) + for k, v := range d.Accounts { + out[k] = types.Account{ + Code: bytes.Clone(v.Code), + Storage: maps.Clone(v.Storage), + Balance: new(big.Int).Set(v.Balance), + Nonce: v.Nonce, + } + } return &ForgeAllocs{Accounts: out} } diff --git a/op-chain-ops/interopgen/deploy.go b/op-chain-ops/interopgen/deploy.go index e915b724e10b..0e084adc4e63 100644 --- a/op-chain-ops/interopgen/deploy.go +++ b/op-chain-ops/interopgen/deploy.go @@ -218,6 +218,7 @@ func DeployL2ToL1(l1Host *script.Host, superCfg *SuperchainConfig, superDeployme DisputeSplitDepth: cfg.DisputeSplitDepth, DisputeClockExtension: cfg.DisputeClockExtension, DisputeMaxClockDuration: cfg.DisputeMaxClockDuration, + StartingAnchorRoots: opcm.PermissionedGameStartingAnchorRoots, }) if err != nil { return nil, fmt.Errorf("failed to deploy L2 OP chain: %w", err) diff --git a/op-deployer/pkg/deployer/artifacts/locator.go b/op-deployer/pkg/deployer/artifacts/locator.go index 42b838f66a7c..044b61641ec5 100644 --- a/op-deployer/pkg/deployer/artifacts/locator.go +++ b/op-deployer/pkg/deployer/artifacts/locator.go @@ -45,6 +45,15 @@ type Locator struct { Tag string } +func NewFileLocator(path string) (*Locator, error) { + u, err := url.Parse("file://" + path) + if err != nil { + return nil, fmt.Errorf("failed to parse URL: %w", err) + } + + return &Locator{URL: u}, nil +} + func (a *Locator) UnmarshalText(text []byte) error { str := string(text) diff --git a/op-deployer/pkg/deployer/inspect/l1.go b/op-deployer/pkg/deployer/inspect/l1.go index 4883e83486c3..25de3810f622 100644 --- a/op-deployer/pkg/deployer/inspect/l1.go +++ b/op-deployer/pkg/deployer/inspect/l1.go @@ -3,6 +3,8 @@ package inspect import ( "fmt" + "github.com/ethereum-optimism/optimism/op-chain-ops/genesis" + "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/pipeline" "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/state" @@ -18,6 +20,33 @@ type L1Contracts struct { ImplementationsDeployment ImplementationsDeployment `json:"implementationsDeployment"` } +func (l L1Contracts) AsL1Deployments() *genesis.L1Deployments { + return &genesis.L1Deployments{ + AddressManager: l.OpChainDeployment.AddressManagerAddress, + DisputeGameFactory: l.ImplementationsDeployment.DisputeGameFactoryImplAddress, + DisputeGameFactoryProxy: l.OpChainDeployment.DisputeGameFactoryProxyAddress, + L1CrossDomainMessenger: l.ImplementationsDeployment.L1CrossDomainMessengerImplAddress, + L1CrossDomainMessengerProxy: l.OpChainDeployment.L1CrossDomainMessengerProxyAddress, + L1ERC721Bridge: l.ImplementationsDeployment.L1ERC721BridgeImplAddress, + L1ERC721BridgeProxy: l.OpChainDeployment.L1ERC721BridgeProxyAddress, + L1StandardBridge: l.ImplementationsDeployment.L1StandardBridgeImplAddress, + L1StandardBridgeProxy: l.OpChainDeployment.L1StandardBridgeProxyAddress, + L2OutputOracle: common.Address{}, + L2OutputOracleProxy: common.Address{}, + OptimismMintableERC20Factory: l.ImplementationsDeployment.OptimismMintableERC20FactoryImplAddress, + OptimismMintableERC20FactoryProxy: l.OpChainDeployment.OptimismMintableERC20FactoryProxyAddress, + OptimismPortal: l.ImplementationsDeployment.OptimismPortalImplAddress, + OptimismPortalProxy: l.OpChainDeployment.OptimismPortalProxyAddress, + ProxyAdmin: l.OpChainDeployment.ProxyAdminAddress, + SystemConfig: l.ImplementationsDeployment.SystemConfigImplAddress, + SystemConfigProxy: l.OpChainDeployment.SystemConfigProxyAddress, + ProtocolVersions: l.SuperchainDeployment.ProtocolVersionsImplAddress, + ProtocolVersionsProxy: l.SuperchainDeployment.ProtocolVersionsProxyAddress, + DataAvailabilityChallenge: l.OpChainDeployment.DataAvailabilityChallengeImplAddress, + DataAvailabilityChallengeProxy: l.OpChainDeployment.DataAvailabilityChallengeProxyAddress, + } +} + type SuperchainDeployment struct { ProxyAdminAddress common.Address `json:"proxyAdminAddress"` SuperchainConfigProxyAddress common.Address `json:"superchainConfigProxyAddress"` @@ -42,6 +71,8 @@ type OpChainDeployment struct { PermissionedDisputeGameAddress common.Address `json:"permissionedDisputeGameAddress"` DelayedWETHPermissionedGameProxyAddress common.Address `json:"delayedWETHPermissionedGameProxyAddress"` // DelayedWETHPermissionlessGameProxyAddress common.Address `json:"delayedWETHPermissionlessGameProxyAddress"` + DataAvailabilityChallengeProxyAddress common.Address `json:"dataAvailabilityChallengeProxyAddress"` + DataAvailabilityChallengeImplAddress common.Address `json:"dataAvailabilityChallengeImplAddress"` } type ImplementationsDeployment struct { @@ -110,6 +141,8 @@ func L1(globalState *state.State, chainID common.Hash) (*L1Contracts, error) { FaultDisputeGameAddress: chainState.FaultDisputeGameAddress, PermissionedDisputeGameAddress: chainState.PermissionedDisputeGameAddress, DelayedWETHPermissionedGameProxyAddress: chainState.DelayedWETHPermissionedGameProxyAddress, + DataAvailabilityChallengeProxyAddress: chainState.DataAvailabilityChallengeProxyAddress, + DataAvailabilityChallengeImplAddress: chainState.DataAvailabilityChallengeImplAddress, // DelayedWETHPermissionlessGameProxyAddress: chainState.DelayedWETHPermissionlessGameProxyAddress, }, ImplementationsDeployment: ImplementationsDeployment{ diff --git a/op-deployer/pkg/deployer/integration_test/apply_test.go b/op-deployer/pkg/deployer/integration_test/apply_test.go index cbcfd1c722ea..830a4bb4f744 100644 --- a/op-deployer/pkg/deployer/integration_test/apply_test.go +++ b/op-deployer/pkg/deployer/integration_test/apply_test.go @@ -182,11 +182,7 @@ func TestApplyExistingOPCM(t *testing.T) { func testApplyExistingOPCM(t *testing.T, l1ChainID uint64, forkRPCUrl string, versions standard.L1Versions) { op_e2e.InitParallel(t) - anvil.Test(t) - - if forkRPCUrl == "" { - t.Skip("no fork RPC URL provided") - } + require.NotEmpty(t, forkRPCUrl, "no fork RPC URL provided") lgr := testlog.Logger(t, slog.LevelDebug) @@ -406,7 +402,6 @@ func testApplyExistingOPCM(t *testing.T, l1ChainID uint64, forkRPCUrl string, ve //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 - require.Equal(t, len(expAllocs), len(actAllocs)) for addr, expAcc := range expAllocs { actAcc, ok := actAllocs[addr] require.True(t, ok) @@ -420,11 +415,17 @@ func testApplyExistingOPCM(t *testing.T, l1ChainID uint64, forkRPCUrl string, ve } 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) { op_e2e.InitParallel(t) - kurtosisutil.Test(t) ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -499,9 +500,9 @@ func TestProofParamOverrides(t *testing.T) { opts, intent, st := setupGenesisChain(t, defaultL1ChainID) intent.GlobalDeployOverrides = map[string]any{ - "withdrawalDelaySeconds": standard.WithdrawalDelaySeconds + 1, - "minProposalSizeBytes": standard.MinProposalSizeBytes + 1, - "challengePeriodSeconds": standard.ChallengePeriodSeconds + 1, + "faultGameWithdrawalDelay": standard.WithdrawalDelaySeconds + 1, + "preimageOracleMinProposalSize": standard.MinProposalSizeBytes + 1, + "preimageOracleChallengePeriod": standard.ChallengePeriodSeconds + 1, "proofMaturityDelaySeconds": standard.ProofMaturityDelaySeconds + 1, "disputeGameFinalityDelaySeconds": standard.DisputeGameFinalityDelaySeconds + 1, "mipsVersion": standard.MIPSVersion + 1, @@ -529,17 +530,17 @@ func TestProofParamOverrides(t *testing.T) { address common.Address }{ { - "withdrawalDelaySeconds", + "faultGameWithdrawalDelay", uint64Caster, st.ImplementationsDeployment.DelayedWETHImplAddress, }, { - "minProposalSizeBytes", + "preimageOracleMinProposalSize", uint64Caster, st.ImplementationsDeployment.PreimageOracleSingletonAddress, }, { - "challengePeriodSeconds", + "preimageOracleChallengePeriod", uint64Caster, st.ImplementationsDeployment.PreimageOracleSingletonAddress, }, @@ -708,7 +709,9 @@ func TestAdditionalDisputeGames(t *testing.T) { defer cancel() opts, intent, st := setupGenesisChain(t, defaultL1ChainID) - (&intent.Chains[0].Roles).L1ProxyAdminOwner = crypto.PubkeyToAddress(opts.DeployerPrivateKey.PublicKey) + deployerAddr := crypto.PubkeyToAddress(opts.DeployerPrivateKey.PublicKey) + (&intent.Chains[0].Roles).L1ProxyAdminOwner = deployerAddr + intent.SuperchainRoles.Guardian = deployerAddr intent.GlobalDeployOverrides = map[string]any{ "challengePeriodSeconds": 1, } @@ -726,6 +729,7 @@ func TestAdditionalDisputeGames(t *testing.T) { UseCustomOracle: true, OracleMinProposalSize: 10000, OracleChallengePeriodSeconds: 120, + MakeRespected: true, VMType: state.VMTypeAlphabet, }, } @@ -848,7 +852,7 @@ func newIntent( ProtocolVersionsOwner: addrFor(t, dk, devkeys.SuperchainDeployerKey.Key(l1ChainID)), Guardian: addrFor(t, dk, devkeys.SuperchainConfigGuardianKey.Key(l1ChainID)), }, - FundDevAccounts: true, + FundDevAccounts: false, L1ContractsLocator: l1Loc, L2ContractsLocator: l2Loc, Chains: []*state.ChainIntent{ diff --git a/op-deployer/pkg/deployer/opcm/anchor_roots.go b/op-deployer/pkg/deployer/opcm/anchor_roots.go new file mode 100644 index 000000000000..1ba47433efd2 --- /dev/null +++ b/op-deployer/pkg/deployer/opcm/anchor_roots.go @@ -0,0 +1,55 @@ +package opcm + +import ( + "fmt" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/lmittmann/w3" +) + +var anchorRootFunc = w3.MustNewFunc(` +dummy((uint32 gameType, (bytes32 root, uint256 l2BlockNumber) outputRoot)[] roots) +`, "") + +type StartingAnchorRoot struct { + GameType uint32 + Root common.Hash + L2BlockNumber *big.Int +} + +var DefaultStartingAnchorRoot = StartingAnchorRoot{ + GameType: 1, + Root: common.Hash{0xde, 0xad}, + L2BlockNumber: common.Big0, +} + +type encodingStartingAnchorRoot struct { + GameType uint32 + 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 { + return nil, fmt.Errorf("error encoding anchor roots: %w", err) + } + // Chop off the function selector since w3 can't serialize structs directly + return encoded[4:], nil +} diff --git a/op-deployer/pkg/deployer/opcm/anchor_roots_test.go b/op-deployer/pkg/deployer/opcm/anchor_roots_test.go new file mode 100644 index 000000000000..3c68caf66346 --- /dev/null +++ b/op-deployer/pkg/deployer/opcm/anchor_roots_test.go @@ -0,0 +1,42 @@ +package opcm + +import ( + "math/big" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/stretchr/testify/require" +) + +func TestEncodeStartingAnchorRoots(t *testing.T) { + encoded, err := EncodeStartingAnchorRoots([]StartingAnchorRoot{ + DefaultStartingAnchorRoot, + }) + require.NoError(t, err) + require.EqualValues(t, PermissionedGameStartingAnchorRoots, encoded) + + encoded, err = EncodeStartingAnchorRoots([]StartingAnchorRoot{ + { + GameType: 0, + L2BlockNumber: common.Big0, + }, + { + GameType: 1, + Root: common.Hash{0xde, 0xad}, + L2BlockNumber: big.NewInt(0), + }, + }) + require.NoError(t, err) + require.EqualValues(t, + common.Hex2Bytes( + "0000000000000000000000000000000000000000000000000000000000000020"+ + "0000000000000000000000000000000000000000000000000000000000000002"+ + "0000000000000000000000000000000000000000000000000000000000000000"+ + "0000000000000000000000000000000000000000000000000000000000000000"+ + "0000000000000000000000000000000000000000000000000000000000000000"+ + "0000000000000000000000000000000000000000000000000000000000000001"+ + "dead000000000000000000000000000000000000000000000000000000000000"+ + "0000000000000000000000000000000000000000000000000000000000000000"), + encoded, + ) +} diff --git a/op-deployer/pkg/deployer/opcm/dispute_game_factory.go b/op-deployer/pkg/deployer/opcm/dispute_game_factory.go index eb34e9f2206b..6e79f0683a94 100644 --- a/op-deployer/pkg/deployer/opcm/dispute_game_factory.go +++ b/op-deployer/pkg/deployer/opcm/dispute_game_factory.go @@ -6,9 +6,11 @@ import ( ) type SetDisputeGameImplInput struct { - Factory common.Address - Impl common.Address - GameType uint32 + Factory common.Address + Impl common.Address + Portal common.Address + AnchorStateRegistry common.Address + GameType uint32 } func SetDisputeGameImpl( diff --git a/op-deployer/pkg/deployer/opcm/l2genesis.go b/op-deployer/pkg/deployer/opcm/l2genesis.go index 8b6e123dad3f..086e5fb099a9 100644 --- a/op-deployer/pkg/deployer/opcm/l2genesis.go +++ b/op-deployer/pkg/deployer/opcm/l2genesis.go @@ -2,6 +2,7 @@ package opcm import ( "fmt" + "time" "github.com/ethereum-optimism/optimism/op-chain-ops/genesis" "github.com/ethereum-optimism/optimism/op-chain-ops/script" @@ -32,6 +33,8 @@ func L2Genesis(l2Host *script.Host, input *L2GenesisInput) error { l2Host.SetEnvVar("L2GENESIS_L1CrossDomainMessengerProxy", input.L1Deployments.L1CrossDomainMessengerProxy.String()) l2Host.SetEnvVar("L2GENESIS_L1StandardBridgeProxy", input.L1Deployments.L1StandardBridgeProxy.String()) l2Host.SetEnvVar("L2GENESIS_L1ERC721BridgeProxy", input.L1Deployments.L1ERC721BridgeProxy.String()) + allocsMode := input.L2Config.UpgradeScheduleDeployConfig.AllocMode(uint64(time.Now().Unix())) + l2Host.SetEnvVar("FORK", string(allocsMode)) deployConfig := &genesis.DeployConfig{ L2InitializationConfig: input.L2Config, diff --git a/op-deployer/pkg/deployer/opcm/mips.go b/op-deployer/pkg/deployer/opcm/mips.go index 77919ffbd796..520412aa102d 100644 --- a/op-deployer/pkg/deployer/opcm/mips.go +++ b/op-deployer/pkg/deployer/opcm/mips.go @@ -11,18 +11,10 @@ type DeployMIPSInput struct { PreimageOracle common.Address } -func (input *DeployMIPSInput) InputSet() bool { - return true -} - type DeployMIPSOutput struct { MipsSingleton common.Address } -func (output *DeployMIPSOutput) CheckOutput(input common.Address) error { - return nil -} - type DeployMIPSScript struct { Run func(input, output common.Address) error } diff --git a/op-deployer/pkg/deployer/opcm/opchain.go b/op-deployer/pkg/deployer/opcm/opchain.go index 69891358dfbe..430b72454481 100644 --- a/op-deployer/pkg/deployer/opcm/opchain.go +++ b/op-deployer/pkg/deployer/opcm/opchain.go @@ -36,6 +36,7 @@ type DeployOPChainInputV160 struct { DisputeSplitDepth uint64 DisputeClockExtension uint64 DisputeMaxClockDuration uint64 + StartingAnchorRoots []byte AllowCustomDisputeParameters bool } @@ -43,10 +44,6 @@ func (input *DeployOPChainInputV160) InputSet() bool { return true } -func (input *DeployOPChainInputV160) StartingAnchorRoots() []byte { - return PermissionedGameStartingAnchorRoots -} - type DeployOPChainInputIsthmus struct { DeployOPChainInputV160 SystemConfigFeeAdmin common.Address diff --git a/op-deployer/pkg/deployer/pipeline/dispute_games.go b/op-deployer/pkg/deployer/pipeline/dispute_games.go index c6ec11e2c3b9..dd95398556cd 100644 --- a/op-deployer/pkg/deployer/pipeline/dispute_games.go +++ b/op-deployer/pkg/deployer/pipeline/dispute_games.go @@ -5,7 +5,6 @@ import ( "math/big" "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/opcm" - "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/standard" "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/state" "github.com/ethereum/go-ethereum/common" ) @@ -67,8 +66,10 @@ func deployDisputeGame( return fmt.Errorf("failed to deploy preimage oracle: %w", err) } oracleAddr = out.PreimageOracle + lgr.Info("oracle deployed", "oracleAddr", oracleAddr) } else { - lgr.Debug("using existing preimage oracle") + lgr.Info("using existing preimage oracle") + oracleAddr = st.ImplementationsDeployment.PreimageOracleSingletonAddress } lgr.Info("deploying VM", "vmType", game.VMType) @@ -77,12 +78,26 @@ func deployDisputeGame( case state.VMTypeAlphabet: out, err := opcm.DeployAlphabetVM(env.L1ScriptHost, opcm.DeployAlphabetVMInput{ AbsolutePrestate: game.DisputeAbsolutePrestate, - PreimageOracle: st.ImplementationsDeployment.PreimageOracleSingletonAddress, + PreimageOracle: oracleAddr, }) if err != nil { return fmt.Errorf("failed to deploy Alphabet VM: %w", err) } vmAddr = out.AlphabetVM + case state.VMTypeCannon1, state.VMTypeCannon2: + mipsVersion := 1 + if game.VMType == state.VMTypeCannon2 { + mipsVersion = 2 + } + + out, err := opcm.DeployMIPS(env.L1ScriptHost, opcm.DeployMIPSInput{ + MipsVersion: uint64(mipsVersion), + PreimageOracle: oracleAddr, + }) + if err != nil { + return fmt.Errorf("failed to deploy MIPS VM: %w", err) + } + vmAddr = out.MipsSingleton default: return fmt.Errorf("unsupported VM type: %v", game.VMType) } @@ -94,7 +109,7 @@ func deployDisputeGame( VmAddress: vmAddr, GameKind: "FaultDisputeGame", GameType: game.DisputeGameType, - AbsolutePrestate: standard.DisputeAbsolutePrestate, + AbsolutePrestate: game.DisputeAbsolutePrestate, MaxGameDepth: game.DisputeMaxGameDepth, SplitDepth: game.DisputeSplitDepth, ClockExtension: game.DisputeClockExtension, @@ -110,13 +125,19 @@ func deployDisputeGame( } lgr.Info("dispute game deployed", "impl", out.DisputeGameImpl) - lgr.Info("setting dispute game impl on factory") + lgr.Info("setting dispute game impl on factory", "respected", game.MakeRespected) + sdgiInput := opcm.SetDisputeGameImplInput{ + Factory: thisState.DisputeGameFactoryProxyAddress, + Impl: out.DisputeGameImpl, + GameType: game.DisputeGameType, + AnchorStateRegistry: thisState.AnchorStateRegistryProxyAddress, + } + if game.MakeRespected { + sdgiInput.Portal = thisState.OptimismPortalProxyAddress + } if err := opcm.SetDisputeGameImpl( env.L1ScriptHost, - opcm.SetDisputeGameImplInput{ - Factory: thisState.DisputeGameFactoryProxyAddress, - Impl: out.DisputeGameImpl, - }, + sdgiInput, ); err != nil { return fmt.Errorf("failed to set dispute game impl: %w", err) } diff --git a/op-deployer/pkg/deployer/pipeline/init.go b/op-deployer/pkg/deployer/pipeline/init.go index 9a8848843719..a5d29786f0c3 100644 --- a/op-deployer/pkg/deployer/pipeline/init.go +++ b/op-deployer/pkg/deployer/pipeline/init.go @@ -26,7 +26,7 @@ func InitLiveStrategy(ctx context.Context, env *Env, intent *state.Intent, st *s lgr := env.Logger.New("stage", "init", "strategy", "live") lgr.Info("initializing pipeline") - if err := initCommonChecks(st); err != nil { + if err := initCommonChecks(intent, st); err != nil { return err } @@ -100,7 +100,7 @@ func InitLiveStrategy(ctx context.Context, env *Env, intent *state.Intent, st *s return nil } -func initCommonChecks(st *state.State) error { +func initCommonChecks(intent *state.Intent, st *state.State) error { // Ensure the state version is supported. if !IsSupportedStateVersion(st.Version) { return fmt.Errorf("unsupported state version: %d", st.Version) @@ -112,6 +112,7 @@ func initCommonChecks(st *state.State) error { return fmt.Errorf("failed to generate CREATE2 salt: %w", err) } } + return nil } @@ -119,7 +120,7 @@ func InitGenesisStrategy(env *Env, intent *state.Intent, st *state.State) error lgr := env.Logger.New("stage", "init", "strategy", "genesis") lgr.Info("initializing pipeline") - if err := initCommonChecks(st); err != nil { + if err := initCommonChecks(intent, st); err != nil { return err } diff --git a/op-deployer/pkg/deployer/pipeline/opchain.go b/op-deployer/pkg/deployer/pipeline/opchain.go index 4bc9603347c5..20d044fbf29a 100644 --- a/op-deployer/pkg/deployer/pipeline/opchain.go +++ b/op-deployer/pkg/deployer/pipeline/opchain.go @@ -106,6 +106,27 @@ func makeDCIV160(intent *state.Intent, thisIntent *state.ChainIntent, chainID co return opcm.DeployOPChainInputV160{}, fmt.Errorf("error merging proof params from overrides: %w", err) } + startingAnchorRoots := opcm.PermissionedGameStartingAnchorRoots + 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.DisputeAbsolutePrestate, + 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, SystemConfigOwner: thisIntent.Roles.SystemConfigOwner, @@ -126,6 +147,7 @@ func makeDCIV160(intent *state.Intent, thisIntent *state.ChainIntent, chainID co DisputeClockExtension: proofParams.DisputeClockExtension, // 3 hours (input in seconds) DisputeMaxClockDuration: proofParams.DisputeMaxClockDuration, // 3.5 days (input in seconds) AllowCustomDisputeParameters: proofParams.DangerouslyAllowCustomDisputeParameters, + StartingAnchorRoots: startingAnchorRoots, }, nil } diff --git a/op-deployer/pkg/deployer/state/chain_intent.go b/op-deployer/pkg/deployer/state/chain_intent.go index c924d3188211..879b402ce878 100644 --- a/op-deployer/pkg/deployer/state/chain_intent.go +++ b/op-deployer/pkg/deployer/state/chain_intent.go @@ -12,6 +12,8 @@ type VMType string const ( VMTypeAlphabet = "ALPHABET" + VMTypeCannon1 = "CANNON1" + VMTypeCannon2 = "CANNON2" ) type ChainProofParams struct { @@ -30,6 +32,7 @@ type AdditionalDisputeGame struct { UseCustomOracle bool OracleMinProposalSize uint64 OracleChallengePeriodSeconds uint64 + MakeRespected bool } type ChainIntent struct { diff --git a/op-deployer/pkg/deployer/state/deploy_config.go b/op-deployer/pkg/deployer/state/deploy_config.go index 72b1cd719e32..c9ad464cc9d4 100644 --- a/op-deployer/pkg/deployer/state/deploy_config.go +++ b/op-deployer/pkg/deployer/state/deploy_config.go @@ -4,6 +4,8 @@ import ( "fmt" "math/big" + op_service "github.com/ethereum-optimism/optimism/op-service" + "github.com/ethereum-optimism/optimism/op-service/jsonutil" "github.com/ethereum/go-ethereum/rpc" @@ -33,6 +35,9 @@ func CombineDeployConfig(intent *Intent, chainIntent *ChainIntent, state *State, ProtocolVersionsProxy: state.SuperchainDeployment.ProtocolVersionsProxyAddress, }, L2InitializationConfig: genesis.L2InitializationConfig{ + DevDeployConfig: genesis.DevDeployConfig{ + FundDevAccounts: intent.FundDevAccounts, + }, L2GenesisBlockDeployConfig: genesis.L2GenesisBlockDeployConfig{ L2GenesisBlockGasLimit: 60_000_000, L2GenesisBlockBaseFeePerGas: &l2GenesisBlockBaseFeePerGas, @@ -69,12 +74,12 @@ func CombineDeployConfig(intent *Intent, chainIntent *ChainIntent, state *State, // In-development hardforks should never be activated here. Instead, they // should be specified as overrides. UpgradeScheduleDeployConfig: genesis.UpgradeScheduleDeployConfig{ - L2GenesisRegolithTimeOffset: u64UtilPtr(0), - L2GenesisCanyonTimeOffset: u64UtilPtr(0), - L2GenesisDeltaTimeOffset: u64UtilPtr(0), - L2GenesisEcotoneTimeOffset: u64UtilPtr(0), - L2GenesisFjordTimeOffset: u64UtilPtr(0), - L2GenesisGraniteTimeOffset: u64UtilPtr(0), + L2GenesisRegolithTimeOffset: op_service.U64UtilPtr(0), + L2GenesisCanyonTimeOffset: op_service.U64UtilPtr(0), + L2GenesisDeltaTimeOffset: op_service.U64UtilPtr(0), + L2GenesisEcotoneTimeOffset: op_service.U64UtilPtr(0), + L2GenesisFjordTimeOffset: op_service.U64UtilPtr(0), + L2GenesisGraniteTimeOffset: op_service.U64UtilPtr(0), UseInterop: intent.UseInterop, }, L2CoreDeployConfig: genesis.L2CoreDeployConfig{ @@ -108,7 +113,7 @@ func CombineDeployConfig(intent *Intent, chainIntent *ChainIntent, state *State, } if intent.UseInterop { - cfg.L2InitializationConfig.UpgradeScheduleDeployConfig.L2GenesisInteropTimeOffset = u64UtilPtr(0) + cfg.L2InitializationConfig.UpgradeScheduleDeployConfig.L2GenesisInteropTimeOffset = op_service.U64UtilPtr(0) } if chainState.StartBlock == nil { @@ -177,11 +182,6 @@ func mustHexBigFromHex(hex string) *hexutil.Big { return &hexBig } -func u64UtilPtr(in uint64) *hexutil.Uint64 { - util := hexutil.Uint64(in) - return &util -} - func calculateBatchInboxAddr(chainID common.Hash) common.Address { var out common.Address copy(out[1:], crypto.Keccak256(chainID[:])[:19]) diff --git a/op-deployer/pkg/deployer/state/intent.go b/op-deployer/pkg/deployer/state/intent.go index 5ed275107a5f..54c07a79cbd2 100644 --- a/op-deployer/pkg/deployer/state/intent.go +++ b/op-deployer/pkg/deployer/state/intent.go @@ -46,9 +46,9 @@ var emptyAddress common.Address var emptyHash common.Hash type SuperchainProofParams struct { - WithdrawalDelaySeconds uint64 `json:"withdrawalDelaySeconds" toml:"withdrawalDelaySeconds"` - MinProposalSizeBytes uint64 `json:"minProposalSizeBytes" toml:"minProposalSizeBytes"` - ChallengePeriodSeconds uint64 `json:"challengePeriodSeconds" toml:"challengePeriodSeconds"` + WithdrawalDelaySeconds uint64 `json:"faultGameWithdrawalDelay" toml:"faultGameWithdrawalDelay"` + MinProposalSizeBytes uint64 `json:"preimageOracleMinProposalSize" toml:"preimageOracleMinProposalSize"` + ChallengePeriodSeconds uint64 `json:"preimageOracleChallengePeriod" toml:"preimageOracleChallengePeriod"` ProofMaturityDelaySeconds uint64 `json:"proofMaturityDelaySeconds" toml:"proofMaturityDelaySeconds"` DisputeGameFinalityDelaySeconds uint64 `json:"disputeGameFinalityDelaySeconds" toml:"disputeGameFinalityDelaySeconds"` MIPSVersion uint64 `json:"mipsVersion" toml:"mipsVersion"` diff --git a/op-e2e/actions/derivation/system_config_test.go b/op-e2e/actions/derivation/system_config_test.go index ecfe466cadb0..d2753ff6bb5e 100644 --- a/op-e2e/actions/derivation/system_config_test.go +++ b/op-e2e/actions/derivation/system_config_test.go @@ -98,19 +98,19 @@ func BatcherKeyRotation(gt *testing.T, deltaTimeOffset *hexutil.Uint64) { sysCfgContract, err := bindings.NewSystemConfig(sd.RollupCfg.L1SystemConfigAddress, miner.EthClient()) require.NoError(t, err) - sysCfgOwner, err := bind.NewKeyedTransactorWithChainID(dp.Secrets.SysCfgOwner, sd.RollupCfg.L1ChainID) + sysCfgOwner, err := bind.NewKeyedTransactorWithChainID(dp.Secrets.Deployer, sd.RollupCfg.L1ChainID) require.NoError(t, err) owner, err := sysCfgContract.Owner(&bind.CallOpts{}) require.NoError(t, err) - require.Equal(t, dp.Addresses.SysCfgOwner, owner, "system config owner mismatch") + require.Equal(t, dp.Addresses.Deployer, owner, "system config owner mismatch") // Change the batch sender key to Bob! tx, err := sysCfgContract.SetBatcherHash(sysCfgOwner, eth.AddressAsLeftPaddedHash(dp.Addresses.Bob)) require.NoError(t, err) t.Logf("batcher changes in L1 tx %s", tx.Hash()) miner.ActL1StartBlock(12)(t) - miner.ActL1IncludeTx(dp.Addresses.SysCfgOwner)(t) + miner.ActL1IncludeTx(dp.Addresses.Deployer)(t) miner.ActL1EndBlock(t) receipt, err := miner.EthClient().TransactionReceipt(t.Ctx(), tx.Hash()) @@ -289,7 +289,7 @@ func GPOParamsChange(gt *testing.T, deltaTimeOffset *hexutil.Uint64) { sysCfgContract, err := bindings.NewSystemConfig(sd.RollupCfg.L1SystemConfigAddress, miner.EthClient()) require.NoError(t, err) - sysCfgOwner, err := bind.NewKeyedTransactorWithChainID(dp.Secrets.SysCfgOwner, sd.RollupCfg.L1ChainID) + sysCfgOwner, err := bind.NewKeyedTransactorWithChainID(dp.Secrets.Deployer, sd.RollupCfg.L1ChainID) require.NoError(t, err) // overhead changes from 2100 (default) to 1000 @@ -300,7 +300,7 @@ func GPOParamsChange(gt *testing.T, deltaTimeOffset *hexutil.Uint64) { // include the GPO change tx in L1 miner.ActL1StartBlock(12)(t) - miner.ActL1IncludeTx(dp.Addresses.SysCfgOwner)(t) + miner.ActL1IncludeTx(dp.Addresses.Deployer)(t) miner.ActL1EndBlock(t) basefeeGPOUpdate := miner.L1Chain().CurrentBlock().BaseFee @@ -384,7 +384,7 @@ func GasLimitChange(gt *testing.T, deltaTimeOffset *hexutil.Uint64) { sysCfgContract, err := bindings.NewSystemConfig(sd.RollupCfg.L1SystemConfigAddress, miner.EthClient()) require.NoError(t, err) - sysCfgOwner, err := bind.NewKeyedTransactorWithChainID(dp.Secrets.SysCfgOwner, sd.RollupCfg.L1ChainID) + sysCfgOwner, err := bind.NewKeyedTransactorWithChainID(dp.Secrets.Deployer, sd.RollupCfg.L1ChainID) require.NoError(t, err) _, err = sysCfgContract.SetGasLimit(sysCfgOwner, oldGasLimit*3) @@ -392,7 +392,7 @@ func GasLimitChange(gt *testing.T, deltaTimeOffset *hexutil.Uint64) { // include the gaslimit update on L1 miner.ActL1StartBlock(12)(t) - miner.ActL1IncludeTx(dp.Addresses.SysCfgOwner)(t) + miner.ActL1IncludeTx(dp.Addresses.Deployer)(t) miner.ActL1EndBlock(t) // build to latest L1, excluding the block that adopts the L1 block with the gaslimit change diff --git a/op-e2e/actions/upgrades/fjord_fork_test.go b/op-e2e/actions/upgrades/fjord_fork_test.go index 934aa411299f..ae0dbfb0b2b6 100644 --- a/op-e2e/actions/upgrades/fjord_fork_test.go +++ b/op-e2e/actions/upgrades/fjord_fork_test.go @@ -46,6 +46,7 @@ func TestFjordNetworkUpgradeTransactions(gt *testing.T) { dp.DeployConfig.L2GenesisEcotoneTimeOffset = &genesisBlock dp.DeployConfig.L2GenesisFjordTimeOffset = &fjordOffset dp.DeployConfig.L2GenesisGraniteTimeOffset = nil + dp.DeployConfig.L2GenesisHoloceneTimeOffset = nil require.NoError(t, dp.DeployConfig.Check(log), "must have valid config") sd := e2eutils.Setup(t, dp, helpers.DefaultAlloc) diff --git a/op-e2e/config/addresses.json.gz b/op-e2e/config/addresses.json.gz new file mode 100644 index 000000000000..19891ac2cd9f Binary files /dev/null and b/op-e2e/config/addresses.json.gz differ diff --git a/op-e2e/config/allocs-l1.json.gz b/op-e2e/config/allocs-l1.json.gz new file mode 100644 index 000000000000..716554482f11 Binary files /dev/null and b/op-e2e/config/allocs-l1.json.gz differ diff --git a/op-e2e/config/allocs-l2-delta.json.gz b/op-e2e/config/allocs-l2-delta.json.gz new file mode 100644 index 000000000000..eefc554028d7 Binary files /dev/null and b/op-e2e/config/allocs-l2-delta.json.gz differ diff --git a/op-e2e/config/allocs-l2-ecotone.json.gz b/op-e2e/config/allocs-l2-ecotone.json.gz new file mode 100644 index 000000000000..84a43f48f511 Binary files /dev/null and b/op-e2e/config/allocs-l2-ecotone.json.gz differ diff --git a/op-e2e/config/allocs-l2-fjord.json.gz b/op-e2e/config/allocs-l2-fjord.json.gz new file mode 100644 index 000000000000..70937974ed23 Binary files /dev/null and b/op-e2e/config/allocs-l2-fjord.json.gz differ diff --git a/op-e2e/config/allocs-l2-granite.json.gz b/op-e2e/config/allocs-l2-granite.json.gz new file mode 100644 index 000000000000..8290920480eb Binary files /dev/null and b/op-e2e/config/allocs-l2-granite.json.gz differ diff --git a/op-e2e/config/allocs-l2-holocene.json.gz b/op-e2e/config/allocs-l2-holocene.json.gz new file mode 100644 index 000000000000..86aa8ead4725 Binary files /dev/null and b/op-e2e/config/allocs-l2-holocene.json.gz differ diff --git a/op-e2e/config/init.go b/op-e2e/config/init.go index 032f70bf2a88..9f46462fae54 100644 --- a/op-e2e/config/init.go +++ b/op-e2e/config/init.go @@ -1,13 +1,28 @@ package config import ( + "compress/gzip" + "context" + "encoding/json" "fmt" "log/slog" + "math/big" "os" - "path/filepath" + "path" "slices" + "sync" "time" + "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer" + "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/artifacts" + "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/inspect" + "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/pipeline" + "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/state" + "github.com/ethereum-optimism/optimism/op-node/rollup" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "golang.org/x/exp/maps" + "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/log" @@ -15,6 +30,8 @@ import ( "github.com/ethereum-optimism/optimism/op-chain-ops/genesis" op_service "github.com/ethereum-optimism/optimism/op-service" oplog "github.com/ethereum-optimism/optimism/op-service/log" + + _ "embed" ) // legacy geth log levels - the geth command line --verbosity flag wasn't @@ -73,10 +90,15 @@ var ( // DeployConfig represents the deploy config used by the system. deployConfigsByType = make(map[AllocType]*genesis.DeployConfig) // EthNodeVerbosity is the (legacy geth) level of verbosity to output - EthNodeVerbosity int + EthNodeVerbosity int = 3 + + // mtx is a lock to protect the above variables + mtx sync.RWMutex ) func L1Allocs(allocType AllocType) *foundry.ForgeAllocs { + mtx.RLock() + defer mtx.RUnlock() allocs, ok := l1AllocsByType[allocType] if !ok { panic(fmt.Errorf("unknown L1 alloc type: %q", allocType)) @@ -85,6 +107,8 @@ func L1Allocs(allocType AllocType) *foundry.ForgeAllocs { } func L1Deployments(allocType AllocType) *genesis.L1Deployments { + mtx.RLock() + defer mtx.RUnlock() deployments, ok := l1DeploymentsByType[allocType] if !ok { panic(fmt.Errorf("unknown L1 deployments type: %q", allocType)) @@ -93,6 +117,8 @@ func L1Deployments(allocType AllocType) *genesis.L1Deployments { } func L2Allocs(allocType AllocType, mode genesis.L2AllocsMode) *foundry.ForgeAllocs { + mtx.RLock() + defer mtx.RUnlock() allocsByType, ok := l2AllocsByType[allocType] if !ok { panic(fmt.Errorf("unknown L2 alloc type: %q", allocType)) @@ -106,6 +132,8 @@ func L2Allocs(allocType AllocType, mode genesis.L2AllocsMode) *foundry.ForgeAllo } func DeployConfig(allocType AllocType) *genesis.DeployConfig { + mtx.RLock() + defer mtx.RUnlock() dc, ok := deployConfigsByType[allocType] if !ok { panic(fmt.Errorf("unknown deploy config type: %q", allocType)) @@ -123,15 +151,13 @@ func init() { panic(err) } - for _, allocType := range allocTypes { - initAllocType(root, allocType) - } - // Setup global logger lvl := log.FromLegacyLevel(EthNodeVerbosity) var handler slog.Handler + var errHandler slog.Handler if lvl > log.LevelCrit { handler = log.DiscardHandler() + errHandler = log.DiscardHandler() } else { if lvl < log.LevelTrace { // clip to trace level lvl = log.LevelTrace @@ -144,69 +170,374 @@ func init() { Color: false, // some CI logs do not handle colors well Format: oplog.FormatTerminal, }) + + errHandler = oplog.NewLogHandler(os.Stderr, oplog.CLIConfig{ + Level: log.LevelError, + Color: false, + Format: oplog.FormatTerminal, + }) } + + // Start at warning level since alloc generation is heavy on the logs, + // which reduces CI performance. + oplog.SetGlobalLogHandler(errHandler) + + for _, allocType := range allocTypes { + if allocType == AllocTypeL2OO { + continue + } + + initAllocType(root, allocType) + } + + configPath := path.Join(root, "op-e2e", "config") + forks := []genesis.L2AllocsMode{ + genesis.L2AllocsHolocene, + genesis.L2AllocsGranite, + genesis.L2AllocsFjord, + genesis.L2AllocsEcotone, + genesis.L2AllocsDelta, + } + + var l2OOAllocsL1 foundry.ForgeAllocs + decompressGzipJSON(path.Join(configPath, "allocs-l1.json.gz"), &l2OOAllocsL1) + l1AllocsByType[AllocTypeL2OO] = &l2OOAllocsL1 + + var l2OOAddresses genesis.L1Deployments + decompressGzipJSON(path.Join(configPath, "addresses.json.gz"), &l2OOAddresses) + l1DeploymentsByType[AllocTypeL2OO] = &l2OOAddresses + + l2OODC := DeployConfig(AllocTypeStandard) + l2OODC.SetDeployments(&l2OOAddresses) + deployConfigsByType[AllocTypeL2OO] = l2OODC + + l2AllocsByType[AllocTypeL2OO] = genesis.L2AllocsModeMap{} + var wg sync.WaitGroup + for _, fork := range forks { + wg.Add(1) + go func(fork genesis.L2AllocsMode) { + defer wg.Done() + var l2OOAllocsL2 foundry.ForgeAllocs + decompressGzipJSON(path.Join(configPath, fmt.Sprintf("allocs-l2-%s.json.gz", fork)), &l2OOAllocsL2) + mtx.Lock() + l2AllocsByType[AllocTypeL2OO][fork] = &l2OOAllocsL2 + mtx.Unlock() + }(fork) + } + wg.Wait() + + // Use regular level going forward. oplog.SetGlobalLogHandler(handler) } func initAllocType(root string, allocType AllocType) { - devnetDir := filepath.Join(root, fmt.Sprintf(".devnet-%s", allocType)) - l1AllocsPath := filepath.Join(devnetDir, "allocs-l1.json") - l2AllocsDir := devnetDir - l1DeploymentsPath := filepath.Join(devnetDir, "addresses.json") - deployConfigPath := filepath.Join(root, "packages", "contracts-bedrock", "deploy-config", "devnetL1.json") - - var missing bool - for _, fp := range []string{devnetDir, l1AllocsPath, l1DeploymentsPath} { - _, err := os.Stat(fp) - if os.IsNotExist(err) { - missing = true - break - } - if err != nil { - panic(err) - } - } - if missing { - log.Warn("allocs file not found, skipping", "allocType", allocType) - return + artifactsPath := path.Join(root, "packages", "contracts-bedrock", "forge-artifacts") + if err := ensureDir(artifactsPath); err != nil { + panic(fmt.Errorf("invalid artifacts path: %w", err)) } - l1Allocs, err := foundry.LoadForgeAllocs(l1AllocsPath) + loc, err := artifacts.NewFileLocator(artifactsPath) if err != nil { - panic(err) + panic(fmt.Errorf("failed to create artifacts locator: %w", err)) + } + + lgr := log.New() + + allocModes := []genesis.L2AllocsMode{ + genesis.L2AllocsHolocene, + genesis.L2AllocsGranite, + genesis.L2AllocsFjord, + genesis.L2AllocsEcotone, + genesis.L2AllocsDelta, } - l1AllocsByType[allocType] = l1Allocs + l2Alloc := make(map[genesis.L2AllocsMode]*foundry.ForgeAllocs) - mustL2Allocs := func(mode genesis.L2AllocsMode) { - name := "allocs-l2-" + string(mode) - allocs, err := foundry.LoadForgeAllocs(filepath.Join(l2AllocsDir, name+".json")) - if err != nil { - panic(err) - } - l2Alloc[mode] = allocs + var wg sync.WaitGroup + + // Corresponds with the Deployer address in cfg.secrets + pk, err := crypto.HexToECDSA("7c852118294e51e653712a81e05800f419141751be58f605c371e15141b007a6") + if err != nil { + panic(fmt.Errorf("failed to parse private key: %w", err)) } - mustL2Allocs(genesis.L2AllocsHolocene) - mustL2Allocs(genesis.L2AllocsGranite) - mustL2Allocs(genesis.L2AllocsFjord) - mustL2Allocs(genesis.L2AllocsEcotone) - mustL2Allocs(genesis.L2AllocsDelta) + deployerAddr := crypto.PubkeyToAddress(pk.PublicKey) + lgr.Info("deployer address", "address", deployerAddr.Hex()) + + for _, mode := range allocModes { + wg.Add(1) + go func(mode genesis.L2AllocsMode) { + defer wg.Done() + + intent := defaultIntent(root, loc, deployerAddr) + if allocType == AllocTypeAltDA { + intent.Chains[0].DangerousAltDAConfig = genesis.AltDADeployConfig{ + UseAltDA: true, + DACommitmentType: "KeccakCommitment", + DAChallengeWindow: 16, + DAResolveWindow: 16, + DABondSize: 1000000, + DAResolverRefundPercentage: 0, + } + } + + baseUpgradeSchedule := map[string]any{ + "l2GenesisRegolithTimeOffset": nil, + "l2GenesisCanyonTimeOffset": nil, + "l2GenesisDeltaTimeOffset": nil, + "l2GenesisEcotoneTimeOffset": nil, + "l2GenesisFjordTimeOffset": nil, + "l2GenesisGraniteTimeOffset": nil, + "l2GenesisHoloceneTimeOffset": nil, + } + + upgradeSchedule := new(genesis.UpgradeScheduleDeployConfig) + upgradeSchedule.ActivateForkAtGenesis(rollup.ForkName(mode)) + upgradeOverridesJSON, err := json.Marshal(upgradeSchedule) + if err != nil { + panic(fmt.Errorf("failed to marshal upgrade schedule: %w", err)) + } + var upgradeOverrides map[string]any + if err := json.Unmarshal(upgradeOverridesJSON, &upgradeOverrides); err != nil { + panic(fmt.Errorf("failed to unmarshal upgrade schedule: %w", err)) + } + maps.Copy(baseUpgradeSchedule, upgradeOverrides) + maps.Copy(intent.GlobalDeployOverrides, baseUpgradeSchedule) + + st := &state.State{ + Version: 1, + } + + if err := deployer.ApplyPipeline( + context.Background(), + deployer.ApplyPipelineOpts{ + L1RPCUrl: "", + DeployerPrivateKey: pk, + Intent: intent, + State: st, + Logger: lgr, + StateWriter: pipeline.NoopStateWriter(), + }, + ); err != nil { + panic(fmt.Errorf("failed to apply pipeline: %w", err)) + } + + mtx.Lock() + l2Alloc[mode] = st.Chains[0].Allocs.Data + mtx.Unlock() + + // This needs to be updated whenever the latest hardfork is changed. + if mode == genesis.L2AllocsGranite { + dc, err := inspect.DeployConfig(st, intent.Chains[0].ID) + if err != nil { + panic(fmt.Errorf("failed to inspect deploy config: %w", err)) + } + + l1Contracts, err := inspect.L1(st, intent.Chains[0].ID) + if err != nil { + panic(fmt.Errorf("failed to inspect L1: %w", err)) + } + l1Deployments := l1Contracts.AsL1Deployments() + + // Set the L1 genesis block timestamp to now + dc.L1GenesisBlockTimestamp = hexutil.Uint64(time.Now().Unix()) + dc.FundDevAccounts = true + // Speed up the in memory tests + dc.L1BlockTime = 2 + dc.L2BlockTime = 1 + dc.SetDeployments(l1Deployments) + mtx.Lock() + deployConfigsByType[allocType] = dc + l1AllocsByType[allocType] = st.L1StateDump.Data + l1DeploymentsByType[allocType] = l1Deployments + mtx.Unlock() + } + }(mode) + } + wg.Wait() l2AllocsByType[allocType] = l2Alloc - l1Deployments, err := genesis.NewL1Deployments(l1DeploymentsPath) +} + +func defaultIntent(root string, loc *artifacts.Locator, deployer common.Address) *state.Intent { + return &state.Intent{ + ConfigType: state.IntentConfigTypeCustom, + DeploymentStrategy: state.DeploymentStrategyGenesis, + L1ChainID: 900, + SuperchainRoles: &state.SuperchainRoles{ + ProxyAdminOwner: deployer, + ProtocolVersionsOwner: deployer, + Guardian: deployer, + }, + FundDevAccounts: true, + L1ContractsLocator: loc, + L2ContractsLocator: loc, + GlobalDeployOverrides: map[string]any{ + "maxSequencerDrift": 300, + "sequencerWindowSize": 200, + "channelTimeout": 120, + "l2OutputOracleSubmissionInterval": 10, + "l2OutputOracleStartingTimestamp": 0, + "l2OutputOracleProposer": "0x70997970C51812dc3A010C7d01b50e0d17dc79C8", + "l2OutputOracleChallenger": "0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65", + "l2GenesisBlockGasLimit": "0x1c9c380", + "l1BlockTime": 6, + "baseFeeVaultMinimumWithdrawalAmount": "0x8ac7230489e80000", + "l1FeeVaultMinimumWithdrawalAmount": "0x8ac7230489e80000", + "sequencerFeeVaultMinimumWithdrawalAmount": "0x8ac7230489e80000", + "baseFeeVaultWithdrawalNetwork": 0, + "l1FeeVaultWithdrawalNetwork": 0, + "sequencerFeeVaultWithdrawalNetwork": 0, + "finalizationPeriodSeconds": 2, + "l2GenesisBlockBaseFeePerGas": "0x1", + "gasPriceOracleOverhead": 2100, + "gasPriceOracleScalar": 1000000, + "gasPriceOracleBaseFeeScalar": 1368, + "gasPriceOracleBlobBaseFeeScalar": 810949, + "l1CancunTimeOffset": "0x0", + "faultGameAbsolutePrestate": "0x03c7ae758795765c6664a5d39bf63841c71ff191e9189522bad8ebff5d4eca98", + "faultGameMaxDepth": 50, + "faultGameClockExtension": 0, + "faultGameMaxClockDuration": 1200, + "faultGameGenesisBlock": 0, + "faultGameGenesisOutputRoot": "0xDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF", + "faultGameSplitDepth": 14, + "faultGameWithdrawalDelay": 604800, + "preimageOracleMinProposalSize": 10000, + "preimageOracleChallengePeriod": 120, + "proofMaturityDelaySeconds": 12, + "disputeGameFinalityDelaySeconds": 6, + }, + Chains: []*state.ChainIntent{ + { + ID: common.BigToHash(big.NewInt(901)), + BaseFeeVaultRecipient: common.HexToAddress("0x14dC79964da2C08b23698B3D3cc7Ca32193d9955"), + L1FeeVaultRecipient: common.HexToAddress("0x23618e81E3f5cdF7f54C3d65f7FBc0aBf5B21E8f"), + SequencerFeeVaultRecipient: common.HexToAddress("0xa0Ee7A142d267C1f36714E4a8F75612F20a79720"), + Eip1559Denominator: 250, + Eip1559DenominatorCanyon: 250, + Eip1559Elasticity: 6, + Roles: state.ChainRoles{ + // Use deployer as L1PAO to deploy additional dispute impls + L1ProxyAdminOwner: deployer, + L2ProxyAdminOwner: deployer, + SystemConfigOwner: deployer, + UnsafeBlockSigner: common.HexToAddress("0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc"), + Batcher: common.HexToAddress("0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC"), + Proposer: common.HexToAddress("0x70997970C51812dc3A010C7d01b50e0d17dc79C8"), + Challenger: common.HexToAddress("0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65"), + }, + AdditionalDisputeGames: []state.AdditionalDisputeGame{ + { + ChainProofParams: state.ChainProofParams{ + // Fast game + DisputeGameType: 254, + DisputeAbsolutePrestate: common.HexToHash("0x03c7ae758795765c6664a5d39bf63841c71ff191e9189522bad8ebff5d4eca98"), + DisputeMaxGameDepth: 14 + 3 + 1, + DisputeSplitDepth: 14, + DisputeClockExtension: 0, + DisputeMaxClockDuration: 0, + }, + VMType: state.VMTypeAlphabet, + UseCustomOracle: true, + OracleMinProposalSize: 10000, + OracleChallengePeriodSeconds: 0, + MakeRespected: true, + }, + { + ChainProofParams: state.ChainProofParams{ + // Alphabet game + DisputeGameType: 255, + DisputeAbsolutePrestate: common.HexToHash("0x03c7ae758795765c6664a5d39bf63841c71ff191e9189522bad8ebff5d4eca98"), + DisputeMaxGameDepth: 14 + 3 + 1, + DisputeSplitDepth: 14, + DisputeClockExtension: 0, + DisputeMaxClockDuration: 1200, + }, + VMType: state.VMTypeAlphabet, + }, + { + ChainProofParams: state.ChainProofParams{ + DisputeGameType: 0, + DisputeAbsolutePrestate: cannonPrestate(root), + DisputeMaxGameDepth: 50, + DisputeSplitDepth: 14, + DisputeClockExtension: 0, + DisputeMaxClockDuration: 1200, + }, + VMType: cannonVMType(), + }, + }, + }, + }, + } +} + +func ensureDir(dirPath string) error { + stat, err := os.Stat(dirPath) if err != nil { - panic(err) + return fmt.Errorf("failed to stat path: %w", err) + } + if !stat.IsDir() { + return fmt.Errorf("path is not a directory") } - l1DeploymentsByType[allocType] = l1Deployments - dc, err := genesis.NewDeployConfig(deployConfigPath) + return nil +} + +func decompressGzipJSON(p string, thing any) { + f, err := os.Open(p) if err != nil { - panic(err) + panic(fmt.Errorf("failed to open file: %w", err)) + } + defer f.Close() + + gzr, err := gzip.NewReader(f) + if err != nil { + panic(fmt.Errorf("failed to create gzip reader: %w", err)) + } + defer gzr.Close() + if err := json.NewDecoder(gzr).Decode(thing); err != nil { + panic(fmt.Errorf("failed to read gzip data: %w", err)) + } +} + +func cannonVMType() state.VMType { + if os.Getenv("USE_MT_CANNON") == "true" { + return state.VMTypeCannon2 + } + return state.VMTypeCannon1 +} + +type prestateFile struct { + Pre string `json:"pre"` +} + +var cannonPrestateCache common.Hash +var cannonPrestateOnce sync.Once + +func cannonPrestate(monorepoRoot string) common.Hash { + var filename string + + if cannonVMType() == state.VMTypeCannon1 { + filename = "prestate-proof-st.json" + } else { + filename = "prestate-proof-mt.json" } - // Set the L1 genesis block timestamp to now - dc.L1GenesisBlockTimestamp = hexutil.Uint64(time.Now().Unix()) - dc.FundDevAccounts = true - // Speed up the in memory tests - dc.L1BlockTime = 2 - dc.L2BlockTime = 1 - dc.SetDeployments(l1Deployments) - deployConfigsByType[allocType] = dc + cannonPrestateOnce.Do(func() { + f, err := os.Open(path.Join(monorepoRoot, "op-program", "bin", filename)) + if err != nil { + log.Warn("error opening prestate file", "err", err) + return + } + defer f.Close() + + var prestate prestateFile + dec := json.NewDecoder(f) + if err := dec.Decode(&prestate); err != nil { + log.Warn("error decoding prestate file", "err", err) + return + } + + cannonPrestateCache = common.HexToHash(prestate.Pre) + }) + + return cannonPrestateCache } diff --git a/op-e2e/system/altda/concurrent_test.go b/op-e2e/system/altda/concurrent_test.go index e53c7f0f811b..ef11a879dc70 100644 --- a/op-e2e/system/altda/concurrent_test.go +++ b/op-e2e/system/altda/concurrent_test.go @@ -22,7 +22,15 @@ func TestBatcherConcurrentAltDARequests(t *testing.T) { numL1TxsExpected := int64(10) cfg := e2esys.DefaultSystemConfig(t) + // Manually configure these since the alt-DA values aren't + // set at all in the standard config unless UseAltDA is set. + // For some reason this test doesn't use the alt DA allocs. cfg.DeployConfig.UseAltDA = true + cfg.DeployConfig.DACommitmentType = "KeccakCommitment" + cfg.DeployConfig.DAChallengeWindow = 16 + cfg.DeployConfig.DAResolveWindow = 16 + cfg.DeployConfig.DABondSize = 1000000 + cfg.DeployConfig.DAResolverRefundPercentage = 0 cfg.BatcherMaxPendingTransactions = 0 // no limit on parallel txs // ensures that batcher txs are as small as possible cfg.BatcherMaxL1TxSizeBytes = derive.FrameV0OverHeadSize + 1 /*version bytes*/ + 1 diff --git a/op-e2e/system/fees/eip1559params_test.go b/op-e2e/system/fees/eip1559params_test.go index e1b1098cac12..e1b41d24448d 100644 --- a/op-e2e/system/fees/eip1559params_test.go +++ b/op-e2e/system/fees/eip1559params_test.go @@ -35,7 +35,7 @@ func TestEIP1559Params(t *testing.T) { // Obtain our sequencer, verifier, and transactor keypair. l1Client := sys.NodeClient("l1") l2Seq := sys.NodeClient("sequencer") - ethPrivKey := cfg.Secrets.SysCfgOwner + ethPrivKey := cfg.Secrets.Deployer _, err = l2Seq.HeaderByNumber(context.Background(), big.NewInt(0)) require.NoError(t, err) diff --git a/op-e2e/system/fees/gaspriceoracle_test.go b/op-e2e/system/fees/gaspriceoracle_test.go index 58a679fa86f0..22ad06bb6133 100644 --- a/op-e2e/system/fees/gaspriceoracle_test.go +++ b/op-e2e/system/fees/gaspriceoracle_test.go @@ -40,7 +40,7 @@ func TestGasPriceOracleFeeUpdates(t *testing.T) { l1Client := sys.NodeClient("l1") l2Seq := sys.NodeClient("sequencer") // l2Verif := sys.NodeClient("verifier") - ethPrivKey := cfg.Secrets.SysCfgOwner + ethPrivKey := cfg.Secrets.Deployer // Bind to the SystemConfig & GasPriceOracle contracts sysconfig, err := legacybindings.NewSystemConfig(cfg.L1Deployments.SystemConfigProxy, l1Client) diff --git a/op-e2e/system/gastoken/gastoken_test.go b/op-e2e/system/gastoken/gastoken_test.go index b5f3f56e2f6c..bde29ddfa52c 100644 --- a/op-e2e/system/gastoken/gastoken_test.go +++ b/op-e2e/system/gastoken/gastoken_test.go @@ -113,14 +113,14 @@ func testCustomGasToken(t *testing.T, allocType config.AllocType) { op_e2e.InitParallel(t) gto := setup(t) checkDeposit(t, gto, false) - setCustomGasToken(t, gto.cfg, gto.sys, gto.weth9Address) + setCustomGasToken(t, gto.cfg, gto.sys, gto.weth9Address, allocType) checkDeposit(t, gto, true) }) t.Run("withdrawal", func(t *testing.T) { op_e2e.InitParallel(t) gto := setup(t) - setCustomGasToken(t, gto.cfg, gto.sys, gto.weth9Address) + setCustomGasToken(t, gto.cfg, gto.sys, gto.weth9Address, allocType) checkDeposit(t, gto, true) checkWithdrawal(t, gto) }) @@ -128,7 +128,7 @@ func testCustomGasToken(t *testing.T, allocType config.AllocType) { t.Run("fee withdrawal", func(t *testing.T) { op_e2e.InitParallel(t) gto := setup(t) - setCustomGasToken(t, gto.cfg, gto.sys, gto.weth9Address) + setCustomGasToken(t, gto.cfg, gto.sys, gto.weth9Address, allocType) checkDeposit(t, gto, true) checkFeeWithdrawal(t, gto, true) }) @@ -139,7 +139,7 @@ func testCustomGasToken(t *testing.T, allocType config.AllocType) { checkL1TokenNameAndSymbol(t, gto, gto.disabledExpectations) checkL2TokenNameAndSymbol(t, gto, gto.disabledExpectations) checkWETHTokenNameAndSymbol(t, gto, gto.disabledExpectations) - setCustomGasToken(t, gto.cfg, gto.sys, gto.weth9Address) + setCustomGasToken(t, gto.cfg, gto.sys, gto.weth9Address, allocType) checkL1TokenNameAndSymbol(t, gto, gto.enabledExpectations) checkL2TokenNameAndSymbol(t, gto, gto.enabledExpectations) checkWETHTokenNameAndSymbol(t, gto, gto.enabledExpectations) @@ -149,7 +149,7 @@ func testCustomGasToken(t *testing.T, allocType config.AllocType) { // setCustomGasToken enables the Custom Gas Token feature on a chain where it wasn't enabled at genesis. // It reads existing parameters from the SystemConfig contract, inserts the supplied cgtAddress and reinitializes that contract. // To do this it uses the ProxyAdmin and StorageSetter from the supplied cfg. -func setCustomGasToken(t *testing.T, cfg e2esys.SystemConfig, sys *e2esys.System, cgtAddress common.Address) { +func setCustomGasToken(t *testing.T, cfg e2esys.SystemConfig, sys *e2esys.System, cgtAddress common.Address, allocType config.AllocType) { l1Client := sys.NodeClient("l1") deployerOpts, err := bind.NewKeyedTransactorWithChainID(cfg.Secrets.Deployer, cfg.L1ChainIDBig()) require.NoError(t, err) @@ -203,7 +203,11 @@ func setCustomGasToken(t *testing.T, cfg e2esys.SystemConfig, sys *e2esys.System // Set up a signer which controls the Proxy Admin. // The deploy config's finalSystemOwner is the owner of the ProxyAdmin as well as the SystemConfig, // so we can use that address for the proxy admin owner. - proxyAdminOwnerOpts, err := bind.NewKeyedTransactorWithChainID(cfg.Secrets.SysCfgOwner, cfg.L1ChainIDBig()) + ownerSecret := cfg.Secrets.Deployer + if allocType == config.AllocTypeL2OO { + ownerSecret = cfg.Secrets.SysCfgOwner + } + proxyAdminOwnerOpts, err := bind.NewKeyedTransactorWithChainID(ownerSecret, cfg.L1ChainIDBig()) require.NoError(t, err) // Execute the upgrade SystemConfigProxy -> StorageSetter via ProxyAdmin diff --git a/op-e2e/system/proofs/system_fpp_test.go b/op-e2e/system/proofs/system_fpp_test.go index 3eb468aaa8fa..0a9119199206 100644 --- a/op-e2e/system/proofs/system_fpp_test.go +++ b/op-e2e/system/proofs/system_fpp_test.go @@ -78,6 +78,7 @@ func applySpanBatchActivation(active bool, dp *genesis.DeployConfig) { dp.L2GenesisEcotoneTimeOffset = nil dp.L2GenesisFjordTimeOffset = nil dp.L2GenesisGraniteTimeOffset = nil + dp.L2GenesisHoloceneTimeOffset = nil } } diff --git a/op-e2e/system/runcfg/p2p_signer_test.go b/op-e2e/system/runcfg/p2p_signer_test.go index 91ebfb941319..302d72ed3fab 100644 --- a/op-e2e/system/runcfg/p2p_signer_test.go +++ b/op-e2e/system/runcfg/p2p_signer_test.go @@ -40,7 +40,7 @@ func TestRuntimeConfigReload(t *testing.T) { require.NoError(t, err) newUnsafeBlocksSigner := common.Address{0x12, 0x23, 0x45} require.NotEqual(t, initialRuntimeConfig.P2PSequencerAddress(), newUnsafeBlocksSigner, "changing to a different address") - opts, err := bind.NewKeyedTransactorWithChainID(cfg.Secrets.SysCfgOwner, cfg.L1ChainIDBig()) + opts, err := bind.NewKeyedTransactorWithChainID(cfg.Secrets.Deployer, cfg.L1ChainIDBig()) require.Nil(t, err) // the unsafe signer address is part of the runtime config tx, err := sysCfgContract.SetUnsafeBlockSigner(opts, newUnsafeBlocksSigner) diff --git a/op-e2e/system/runcfg/protocol_versions_test.go b/op-e2e/system/runcfg/protocol_versions_test.go index 14e056b04435..91e9a9df20de 100644 --- a/op-e2e/system/runcfg/protocol_versions_test.go +++ b/op-e2e/system/runcfg/protocol_versions_test.go @@ -49,8 +49,8 @@ func TestRecommendedProtocolVersionChange(t *testing.T) { protVersions, err := bindings.NewProtocolVersions(cfg.L1Deployments.ProtocolVersionsProxy, l1) require.NoError(t, err) - // ProtocolVersions contract is owned by same key as SystemConfig in devnet - opts, err := bind.NewKeyedTransactorWithChainID(cfg.Secrets.SysCfgOwner, cfg.L1ChainIDBig()) + // ProtocolVersions contract is owned by the deployer in devnet + opts, err := bind.NewKeyedTransactorWithChainID(cfg.Secrets.Deployer, cfg.L1ChainIDBig()) require.NoError(t, err) // Change recommended protocol version @@ -105,7 +105,7 @@ func TestRequiredProtocolVersionChangeAndHalt(t *testing.T) { require.NoError(t, err) // ProtocolVersions contract is owned by same key as SystemConfig in devnet - opts, err := bind.NewKeyedTransactorWithChainID(cfg.Secrets.SysCfgOwner, cfg.L1ChainIDBig()) + opts, err := bind.NewKeyedTransactorWithChainID(cfg.Secrets.Deployer, cfg.L1ChainIDBig()) require.NoError(t, err) // Change required protocol version diff --git a/op-service/util.go b/op-service/util.go index 5c42d3de5dde..84d8e0d864c4 100644 --- a/op-service/util.go +++ b/op-service/util.go @@ -12,6 +12,8 @@ import ( "strings" "time" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum-optimism/optimism/op-service/ctxinterrupt" "github.com/ethereum/go-ethereum/common" @@ -163,3 +165,8 @@ func Parse256BitChainID(in string) (common.Hash, error) { return common.BigToHash(chainIDBig), nil } + +func U64UtilPtr(in uint64) *hexutil.Uint64 { + util := hexutil.Uint64(in) + return &util +} diff --git a/packages/contracts-bedrock/scripts/deploy/DeployMIPS.s.sol b/packages/contracts-bedrock/scripts/deploy/DeployMIPS.s.sol index 4c32be812869..4db9b6158019 100644 --- a/packages/contracts-bedrock/scripts/deploy/DeployMIPS.s.sol +++ b/packages/contracts-bedrock/scripts/deploy/DeployMIPS.s.sol @@ -63,32 +63,17 @@ contract DeployMIPSOutput is BaseDeployIO { } } - function checkOutput(DeployMIPSInput _mi) public view { - DeployUtils.assertValidContractAddress(address(_mipsSingleton)); - assertValidDeploy(_mi); - } - function mipsSingleton() public view returns (IMIPS) { DeployUtils.assertValidContractAddress(address(_mipsSingleton)); return _mipsSingleton; } - - function assertValidDeploy(DeployMIPSInput _mi) public view { - assertValidMipsSingleton(_mi); - } - - function assertValidMipsSingleton(DeployMIPSInput _mi) internal view { - IMIPS mips = mipsSingleton(); - - require(address(mips.oracle()) == address(_mi.preimageOracle()), "MIPS-10"); - } } /// @title DeployMIPS contract DeployMIPS is Script { function run(DeployMIPSInput _mi, DeployMIPSOutput _mo) public { deployMipsSingleton(_mi, _mo); - _mo.checkOutput(_mi); + assertValidDeploy(_mi, _mo); } function deployMipsSingleton(DeployMIPSInput _mi, DeployMIPSOutput _mo) internal { @@ -106,4 +91,14 @@ contract DeployMIPS is Script { vm.label(address(singleton), "MIPSSingleton"); _mo.set(_mo.mipsSingleton.selector, address(singleton)); } + + function assertValidDeploy(DeployMIPSInput _mi, DeployMIPSOutput _mo) public view { + DeployUtils.assertValidContractAddress(address(_mo.mipsSingleton())); + assertValidMipsSingleton(_mi, _mo); + } + + function assertValidMipsSingleton(DeployMIPSInput _mi, DeployMIPSOutput _mo) internal view { + IMIPS mips = _mo.mipsSingleton(); + require(address(mips.oracle()) == address(_mi.preimageOracle()), "MIPS-10"); + } } diff --git a/packages/contracts-bedrock/scripts/deploy/SetDisputeGameImpl.s.sol b/packages/contracts-bedrock/scripts/deploy/SetDisputeGameImpl.s.sol index a5fab1465cde..17e3d5a99dce 100644 --- a/packages/contracts-bedrock/scripts/deploy/SetDisputeGameImpl.s.sol +++ b/packages/contracts-bedrock/scripts/deploy/SetDisputeGameImpl.s.sol @@ -3,14 +3,16 @@ pragma solidity 0.8.15; import { Script } from "forge-std/Script.sol"; -import { IDisputeGame } from "interfaces/dispute/IDisputeGame.sol"; +import { IFaultDisputeGame } from "interfaces/dispute/IFaultDisputeGame.sol"; import { IDisputeGameFactory } from "interfaces/dispute/IDisputeGameFactory.sol"; import { BaseDeployIO } from "scripts/deploy/BaseDeployIO.sol"; import { GameType } from "src/dispute/lib/Types.sol"; +import { IOptimismPortal2 } from "interfaces/L1/IOptimismPortal2.sol"; contract SetDisputeGameImplInput is BaseDeployIO { IDisputeGameFactory internal _factory; - IDisputeGame internal _impl; + IOptimismPortal2 internal _portal; + IFaultDisputeGame internal _impl; uint32 internal _gameType; // Setter for address type @@ -18,7 +20,8 @@ contract SetDisputeGameImplInput is BaseDeployIO { require(_addr != address(0), "SetDisputeGameImplInput: cannot set zero address"); if (_sel == this.factory.selector) _factory = IDisputeGameFactory(_addr); - else if (_sel == this.impl.selector) _impl = IDisputeGame(_addr); + else if (_sel == this.portal.selector) _portal = IOptimismPortal2(payable(_addr)); + else if (_sel == this.impl.selector) _impl = IFaultDisputeGame(_addr); else revert("SetDisputeGameImplInput: unknown selector"); } @@ -28,13 +31,16 @@ contract SetDisputeGameImplInput is BaseDeployIO { else revert("SetDisputeGameImplInput: unknown selector"); } - // Getters function factory() public view returns (IDisputeGameFactory) { require(address(_factory) != address(0), "SetDisputeGameImplInput: not set"); return _factory; } - function impl() public view returns (IDisputeGame) { + function portal() public view returns (IOptimismPortal2) { + return _portal; + } + + function impl() public view returns (IFaultDisputeGame) { require(address(_impl) != address(0), "SetDisputeGameImplInput: not set"); return _impl; } @@ -50,14 +56,28 @@ contract SetDisputeGameImpl is Script { GameType gameType = GameType.wrap(_input.gameType()); require(address(factory.gameImpls(gameType)) == address(0), "SDGI-10"); - IDisputeGame impl = _input.impl(); + IFaultDisputeGame impl = _input.impl(); + IOptimismPortal2 portal = _input.portal(); + vm.broadcast(msg.sender); factory.setImplementation(gameType, impl); + + if (address(portal) != address(0)) { + require(address(portal.disputeGameFactory()) == address(factory), "SDGI-20"); + vm.broadcast(msg.sender); + portal.setRespectedGameType(gameType); + } + assertValid(_input); } function assertValid(SetDisputeGameImplInput _input) public view { GameType gameType = GameType.wrap(_input.gameType()); - require(address(_input.factory().gameImpls(gameType)) == address(_input.impl()), "SDGI-20"); + require(address(_input.factory().gameImpls(gameType)) == address(_input.impl()), "SDGI-30"); + + if (address(_input.portal()) != address(0)) { + require(address(_input.portal().disputeGameFactory()) == address(_input.factory()), "SDGI-40"); + require(GameType.unwrap(_input.portal().respectedGameType()) == GameType.unwrap(gameType), "SDGI-50"); + } } } diff --git a/packages/contracts-bedrock/test/opcm/SetDisputeGameImpl.t.sol b/packages/contracts-bedrock/test/opcm/SetDisputeGameImpl.t.sol index 7477350a4f62..2dca24b486bd 100644 --- a/packages/contracts-bedrock/test/opcm/SetDisputeGameImpl.t.sol +++ b/packages/contracts-bedrock/test/opcm/SetDisputeGameImpl.t.sol @@ -8,6 +8,11 @@ import { GameType } from "src/dispute/lib/Types.sol"; import { SetDisputeGameImpl, SetDisputeGameImplInput } from "scripts/deploy/SetDisputeGameImpl.s.sol"; import { DisputeGameFactory } from "src/dispute/DisputeGameFactory.sol"; import { Proxy } from "src/universal/Proxy.sol"; +import { OptimismPortal2 } from "src/L1/OptimismPortal2.sol"; +import { IOptimismPortal2 } from "interfaces/L1/IOptimismPortal2.sol"; +import { ISystemConfig } from "interfaces/L1/ISystemConfig.sol"; +import { ISuperchainConfig } from "interfaces/L1/ISuperchainConfig.sol"; +import { SuperchainConfig } from "src/L1/SuperchainConfig.sol"; contract SetDisputeGameImplInput_Test is Test { SetDisputeGameImplInput input; @@ -65,19 +70,43 @@ contract SetDisputeGameImpl_Test is Test { SetDisputeGameImpl script; SetDisputeGameImplInput input; IDisputeGameFactory factory; + IOptimismPortal2 portal; address mockImpl; uint32 gameType; function setUp() public { script = new SetDisputeGameImpl(); input = new SetDisputeGameImplInput(); - DisputeGameFactory impl = new DisputeGameFactory(); + DisputeGameFactory dgfImpl = new DisputeGameFactory(); + OptimismPortal2 portalImpl = new OptimismPortal2(0, 0); + SuperchainConfig supConfigImpl = new SuperchainConfig(); - // Needs to be a proxy to properly initialize - Proxy proxy = new Proxy(address(1)); + Proxy supConfigProxy = new Proxy(address(1)); vm.prank(address(1)); - proxy.upgradeToAndCall(address(impl), abi.encodeCall(impl.initialize, address(this))); - factory = IDisputeGameFactory(address(proxy)); + supConfigProxy.upgradeToAndCall( + address(supConfigImpl), abi.encodeCall(supConfigImpl.initialize, (address(this), false)) + ); + + Proxy factoryProxy = new Proxy(address(1)); + vm.prank(address(1)); + factoryProxy.upgradeToAndCall(address(dgfImpl), abi.encodeCall(dgfImpl.initialize, (address(this)))); + factory = IDisputeGameFactory(address(factoryProxy)); + + Proxy portalProxy = new Proxy(address(1)); + vm.prank(address(1)); + portalProxy.upgradeToAndCall( + address(portalImpl), + abi.encodeCall( + portalImpl.initialize, + ( + factory, + ISystemConfig(makeAddr("sysConfig")), + ISuperchainConfig(address(supConfigProxy)), + GameType.wrap(100) + ) + ) + ); + portal = IOptimismPortal2(payable(address(portalProxy))); mockImpl = makeAddr("impl"); gameType = 999; @@ -86,6 +115,7 @@ contract SetDisputeGameImpl_Test is Test { function test_run_succeeds() public { input.set(input.factory.selector, address(factory)); input.set(input.impl.selector, mockImpl); + input.set(input.portal.selector, address(portal)); input.set(input.gameType.selector, gameType); script.run(input); @@ -94,6 +124,7 @@ contract SetDisputeGameImpl_Test is Test { function test_run_whenImplAlreadySet_reverts() public { input.set(input.factory.selector, address(factory)); input.set(input.impl.selector, mockImpl); + input.set(input.portal.selector, address(portal)); input.set(input.gameType.selector, gameType); // First run should succeed @@ -115,7 +146,7 @@ contract SetDisputeGameImpl_Test is Test { vm.broadcast(address(this)); factory.setImplementation(GameType.wrap(gameType), IDisputeGame(address(0))); - vm.expectRevert("SDGI-20"); + vm.expectRevert("SDGI-30"); script.assertValid(input); } }