Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions op-deployer/cmd/op-deployer/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer"
"github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/bootstrap"
"github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/inspect"
"github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/manage"
"github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/version"

opservice "github.com/ethereum-optimism/optimism/op-service"
Expand Down Expand Up @@ -73,6 +74,11 @@ func main() {
Flags: cliapp.ProtectFlags(deployer.VerifyFlags),
Action: verify.VerifyCLI,
},
{
Name: "manage",
Usage: "manages the chain",
Subcommands: manage.Commands,
},
}
app.Writer = os.Stdout
app.ErrWriter = os.Stderr
Expand Down
143 changes: 143 additions & 0 deletions op-deployer/pkg/deployer/manage/add_game_type.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
package manage

import (
"context"
"encoding/json"
"fmt"
"os"

"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/broadcaster"
"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-service/ctxinterrupt"
oplog "github.com/ethereum-optimism/optimism/op-service/log"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/rpc"
"github.com/urfave/cli/v2"
)

type AddGameTypeConfig struct {
L1RPCUrl string
Logger log.Logger
ArtifactsLocator *artifacts.Locator
Input opcm.AddGameTypeInput
CacheDir string
}

func (c *AddGameTypeConfig) Check() error {
if c.L1RPCUrl == "" {
return fmt.Errorf("l1RPCUrl must be specified")
}

if c.Logger == nil {
return fmt.Errorf("logger must be specified")
}

if c.ArtifactsLocator == nil {
return fmt.Errorf("artifacts locator must be specified")
}

return nil
}

func AddGameTypeCLI(cliCtx *cli.Context) error {
logCfg := oplog.ReadCLIConfig(cliCtx)
l := oplog.NewLogger(oplog.AppOut(cliCtx), logCfg)
oplog.SetGlobalLogHandler(l.Handler())

l1RPCUrl := cliCtx.String(deployer.L1RPCURLFlagName)
configFile := cliCtx.String(ConfigFlag.Name)
artifactsLocatorStr := cliCtx.String(deployer.ArtifactsLocatorFlag.Name)
cacheDir := cliCtx.String(deployer.CacheDirFlag.Name)

artifactsLocator := new(artifacts.Locator)
if err := artifactsLocator.UnmarshalText([]byte(artifactsLocatorStr)); err != nil {
return fmt.Errorf("failed to parse artifacts locator: %w", err)
}

// Read the input configuration from file
configData, err := os.ReadFile(configFile)
if err != nil {
return fmt.Errorf("failed to read config file: %w", err)
}

var input opcm.AddGameTypeInput
if err := json.Unmarshal(configData, &input); err != nil {
return fmt.Errorf("failed to parse config file: %w", err)
}

ctx := ctxinterrupt.WithCancelOnInterrupt(cliCtx.Context)

_, calldata, err := AddGameType(ctx, AddGameTypeConfig{
L1RPCUrl: l1RPCUrl,
Logger: l,
ArtifactsLocator: artifactsLocator,
Input: input,
CacheDir: cacheDir,
})
if err != nil {
return fmt.Errorf("failed to add game type: %w", err)
}

enc := json.NewEncoder(os.Stdout)
enc.SetIndent("", " ")
if err := enc.Encode(calldata); err != nil {
return fmt.Errorf("failed to encode calldata: %w", err)
}

return nil
}

func AddGameType(ctx context.Context, cfg AddGameTypeConfig) (opcm.AddGameTypeOutput, []broadcaster.CalldataDump, error) {
var output opcm.AddGameTypeOutput
if err := cfg.Check(); err != nil {
return output, nil, fmt.Errorf("invalid config for AddGameType: %w", err)
}

lgr := cfg.Logger

artifactsFS, err := artifacts.Download(ctx, cfg.ArtifactsLocator, artifacts.BarProgressor(), cfg.CacheDir)
if err != nil {
return output, nil, fmt.Errorf("failed to download artifacts: %w", err)
}

bcaster := new(broadcaster.CalldataBroadcaster)

l1RPC, err := rpc.Dial(cfg.L1RPCUrl)
if err != nil {
return output, nil, fmt.Errorf("failed to connect to L1 RPC: %w", err)
}

l1Host, err := env.DefaultForkedScriptHost(
ctx,
bcaster,
lgr,
common.Address{'D'},
artifactsFS,
l1RPC,
)
if err != nil {
return output, nil, fmt.Errorf("failed to create script host: %w", err)
}

script, err := opcm.NewAddGameTypeScript(l1Host)
if err != nil {
return output, nil, fmt.Errorf("failed to create L2 genesis script: %w", err)
}

output, err = script.Run(cfg.Input)
if err != nil {
return output, nil, fmt.Errorf("error adding game type: %w", err)
}

// Get the calldata
calldata, err := bcaster.Dump()
if err != nil {
return output, nil, fmt.Errorf("failed to get calldata: %w", err)
}

return output, calldata, nil
}
72 changes: 72 additions & 0 deletions op-deployer/pkg/deployer/manage/add_game_type_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package manage

import (
"context"
"log/slog"
"math/big"
"os"
"testing"
"time"

"github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/standard"
"github.com/ethereum/go-ethereum/superchain"

"github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/opcm"
"github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/testutil"
"github.com/ethereum-optimism/optimism/op-service/testlog"
"github.com/ethereum-optimism/optimism/op-service/testutils"
"github.com/ethereum-optimism/superchain-registry/validation"
"github.com/ethereum/go-ethereum/common"
"github.com/stretchr/testify/require"
)

func TestAddGameType(t *testing.T) {
rpcURL := os.Getenv("SEPOLIA_RPC_URL")
require.NotEmpty(t, rpcURL, "must specify RPC url via SEPOLIA_RPC_URL env var")

afacts, _ := testutil.LocalArtifacts(t)
v200SepoliaAddrs := validation.StandardVersionsSepolia[standard.ContractsV200Tag]
testCacheDir := testutils.IsolatedTestDirWithAutoCleanup(t)

supChain, err := superchain.GetChain(11155420)
require.NoError(t, err)
supChainConfig, err := supChain.Config()
require.NoError(t, err)

cfg := AddGameTypeConfig{
L1RPCUrl: rpcURL,
Logger: testlog.Logger(t, slog.LevelInfo),
ArtifactsLocator: afacts,
Input: opcm.AddGameTypeInput{
SaltMixer: "foo",
// The values below were pulled from the Superchain Registry for OP Sepolia.
SystemConfigProxy: *supChainConfig.Addresses.SystemConfigProxy,
OPChainProxyAdmin: *supChainConfig.Addresses.ProxyAdmin,
DelayedWETHProxy: *supChainConfig.Addresses.DelayedWETHProxy,
DisputeGameType: 999,
DisputeAbsolutePrestate: common.HexToHash("0x1234"),
DisputeMaxGameDepth: big.NewInt(73),
DisputeSplitDepth: big.NewInt(30),
DisputeClockExtension: 10800,
DisputeMaxClockDuration: 302400,
InitialBond: big.NewInt(0),
VM: common.Address(*v200SepoliaAddrs.Mips.Address),
Permissioned: false,
Prank: *supChainConfig.Roles.ProxyAdminOwner,
OPCMImpl: common.Address(*v200SepoliaAddrs.OPContractsManager.Address),
},
CacheDir: testCacheDir,
}

ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
output, broadcasts, err := AddGameType(ctx, cfg)
require.NoError(t, err)

require.Equal(t, 1, len(broadcasts))
// Selector for addGameType
require.EqualValues(t, []byte{0x16, 0x61, 0xa2, 0xe9}, broadcasts[0].Data[0:4])

require.NotEqual(t, common.Address{}, output.DelayedWETHProxy)
require.NotEqual(t, common.Address{}, output.FaultDisputeGameProxy)
}
27 changes: 27 additions & 0 deletions op-deployer/pkg/deployer/manage/flags.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package manage

import (
"github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer"
oplog "github.com/ethereum-optimism/optimism/op-service/log"
"github.com/urfave/cli/v2"
)

var (
ConfigFlag = &cli.StringFlag{
Name: "config",
Usage: "path to the config file",
}
)

var Commands = cli.Commands{
&cli.Command{
Name: "add-game-type",
Usage: "adds a new game type to the chain",
Flags: append([]cli.Flag{
deployer.L1RPCURLFlag,
deployer.ArtifactsLocatorFlag,
ConfigFlag,
}, oplog.CLIFlags(deployer.EnvVarPrefix)...),
Action: AddGameTypeCLI,
},
}
124 changes: 124 additions & 0 deletions op-deployer/pkg/deployer/opcm/add_game_type.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
package opcm

import (
"encoding/json"
"math/big"

"github.com/ethereum-optimism/optimism/op-chain-ops/script"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
)

type AddGameTypeInput struct {
Prank common.Address
OPCMImpl common.Address `abi:"opcmImpl"`
SystemConfigProxy common.Address
OPChainProxyAdmin common.Address `abi:"opChainProxyAdmin"`
DelayedWETHProxy common.Address
DisputeGameType uint32
DisputeAbsolutePrestate common.Hash
DisputeMaxGameDepth *big.Int
DisputeSplitDepth *big.Int
DisputeClockExtension uint64
DisputeMaxClockDuration uint64
InitialBond *big.Int
VM common.Address `abi:"vm"`
Permissioned bool
SaltMixer string
}

type addGameTypeInputJSON struct {
Prank common.Address `json:"prank"`
OPCMImpl common.Address `json:"opcmimpl"`
SystemConfigProxy common.Address `json:"systemConfigProxy"`
OPChainProxyAdmin common.Address `json:"opChainProxyAdmin"`
DelayedWETHProxy common.Address `json:"delayedWETHProxy"`
DisputeGameType uint32 `json:"disputeGameType"`
DisputeAbsolutePrestate common.Hash `json:"disputeAbsolutePrestate"`
DisputeMaxGameDepth *hexutil.Big `json:"disputeMaxGameDepth"`
DisputeSplitDepth *hexutil.Big `json:"disputeSplitDepth"`
DisputeClockExtension uint64 `json:"disputeClockExtension"`
DisputeMaxClockDuration uint64 `json:"disputeMaxClockDuration"`
InitialBond *hexutil.Big `json:"initialBond"`
VM common.Address `json:"vm"`
Permissioned bool `json:"permissioned"`
SaltMixer string `json:"saltMixer"`
}

func (a *AddGameTypeInput) UnmarshalJSON(b []byte) error {
var alias addGameTypeInputJSON
if err := json.Unmarshal(b, &alias); err != nil {
return err
}

a.Prank = alias.Prank
a.OPCMImpl = alias.OPCMImpl
a.SystemConfigProxy = alias.SystemConfigProxy
a.OPChainProxyAdmin = alias.OPChainProxyAdmin
a.DelayedWETHProxy = alias.DelayedWETHProxy
a.DisputeGameType = alias.DisputeGameType
a.DisputeAbsolutePrestate = alias.DisputeAbsolutePrestate

if alias.DisputeMaxGameDepth != nil {
a.DisputeMaxGameDepth = (*big.Int)(alias.DisputeMaxGameDepth)
}

if alias.DisputeSplitDepth != nil {
a.DisputeSplitDepth = (*big.Int)(alias.DisputeSplitDepth)
}

a.DisputeClockExtension = alias.DisputeClockExtension
a.DisputeMaxClockDuration = alias.DisputeMaxClockDuration

if alias.InitialBond != nil {
a.InitialBond = (*big.Int)(alias.InitialBond)
}

a.VM = alias.VM
a.Permissioned = alias.Permissioned
a.SaltMixer = alias.SaltMixer

return nil
}

func (a AddGameTypeInput) MarshalJSON() ([]byte, error) {
alias := addGameTypeInputJSON{
Prank: a.Prank,
OPCMImpl: a.OPCMImpl,
SystemConfigProxy: a.SystemConfigProxy,
OPChainProxyAdmin: a.OPChainProxyAdmin,
DelayedWETHProxy: a.DelayedWETHProxy,
DisputeGameType: a.DisputeGameType,
DisputeAbsolutePrestate: a.DisputeAbsolutePrestate,
DisputeClockExtension: a.DisputeClockExtension,
DisputeMaxClockDuration: a.DisputeMaxClockDuration,
VM: a.VM,
Permissioned: a.Permissioned,
SaltMixer: a.SaltMixer,
}

if a.DisputeMaxGameDepth != nil {
alias.DisputeMaxGameDepth = (*hexutil.Big)(a.DisputeMaxGameDepth)
}

if a.DisputeSplitDepth != nil {
alias.DisputeSplitDepth = (*hexutil.Big)(a.DisputeSplitDepth)
}

if a.InitialBond != nil {
alias.InitialBond = (*hexutil.Big)(a.InitialBond)
}

return json.Marshal(alias)
}

type AddGameTypeOutput struct {
DelayedWETHProxy common.Address `json:"delayedWETHProxy"`
FaultDisputeGameProxy common.Address `json:"faultDisputeGameProxy"`
}

type AddGameTypeScript script.DeployScriptWithOutput[AddGameTypeInput, AddGameTypeOutput]

func NewAddGameTypeScript(host *script.Host) (AddGameTypeScript, error) {
return script.NewDeployScriptWithOutputFromFile[AddGameTypeInput, AddGameTypeOutput](host, "AddGameType.s.sol", "AddGameType")
}
Loading