From 12a38d0d1745f0986d59d0dbbd438e1a4f36a271 Mon Sep 17 00:00:00 2001 From: Adrian Sutton <adrian@oplabs.co> Date: Thu, 26 Sep 2024 11:00:48 +1000 Subject: [PATCH] op-challenger: Skip prestate verifications for the permissioned game. (#12140) Simplifies deployment with minimal risk given that only permissioned actors are involved in the game and typically the challenger is only resolving games. --- op-challenger/game/fault/register_task.go | 16 ++++++--- op-e2e/e2eutils/challenger/helper.go | 21 +++++++++++- op-e2e/e2eutils/disputegame/helper.go | 40 ++++++++++++++++++----- op-e2e/faultproofs/permissioned_test.go | 35 ++++++++++++++++++++ 4 files changed, 99 insertions(+), 13 deletions(-) create mode 100644 op-e2e/faultproofs/permissioned_test.go diff --git a/op-challenger/game/fault/register_task.go b/op-challenger/game/fault/register_task.go index 3b438ad8e..dd346f420 100644 --- a/op-challenger/game/fault/register_task.go +++ b/op-challenger/game/fault/register_task.go @@ -30,7 +30,8 @@ import ( ) type RegisterTask struct { - gameType faultTypes.GameType + gameType faultTypes.GameType + skipPrestateValidation bool getPrestateProvider func(prestateHash common.Hash) (faultTypes.PrestateProvider, error) newTraceAccessor func( @@ -51,6 +52,10 @@ func NewCannonRegisterTask(gameType faultTypes.GameType, cfg *config.Config, m c stateConverter := cannon.NewStateConverter() return &RegisterTask{ gameType: gameType, + // Don't validate the absolute prestate or genesis output root for permissioned games + // Only trusted actors participate in these games so they aren't expected to reach the step() call and + // are often configured without valid prestates but the challenger should still resolve the games. + skipPrestateValidation: gameType == faultTypes.PermissionedGameType, getPrestateProvider: cachePrestates( gameType, stateConverter, @@ -244,9 +249,12 @@ func (e *RegisterTask) Register( } return accessor, nil } - prestateValidator := NewPrestateValidator(e.gameType.String(), contract.GetAbsolutePrestateHash, vmPrestateProvider) - startingValidator := NewPrestateValidator("output root", contract.GetStartingRootHash, prestateProvider) - return NewGamePlayer(ctx, systemClock, l1Clock, logger, m, dir, game.Proxy, txSender, contract, syncValidator, []Validator{prestateValidator, startingValidator}, creator, l1HeaderSource, selective, claimants) + var validators []Validator + if !e.skipPrestateValidation { + validators = append(validators, NewPrestateValidator(e.gameType.String(), contract.GetAbsolutePrestateHash, vmPrestateProvider)) + validators = append(validators, NewPrestateValidator("output root", contract.GetStartingRootHash, prestateProvider)) + } + return NewGamePlayer(ctx, systemClock, l1Clock, logger, m, dir, game.Proxy, txSender, contract, syncValidator, validators, creator, l1HeaderSource, selective, claimants) } err := registerOracle(ctx, m, oracles, gameFactory, caller, e.gameType) if err != nil { diff --git a/op-e2e/e2eutils/challenger/helper.go b/op-e2e/e2eutils/challenger/helper.go index 87a51d96a..177fd90a9 100644 --- a/op-e2e/e2eutils/challenger/helper.go +++ b/op-e2e/e2eutils/challenger/helper.go @@ -58,7 +58,7 @@ func NewHelper(log log.Logger, t *testing.T, require *require.Assertions, dir st } } -type Option func(config2 *config.Config) +type Option func(c *config.Config) func WithFactoryAddress(addr common.Address) Option { return func(c *config.Config) { @@ -84,6 +84,18 @@ func WithPollInterval(pollInterval time.Duration) Option { } } +func WithValidPrestateRequired() Option { + return func(c *config.Config) { + c.AllowInvalidPrestate = false + } +} + +func WithInvalidCannonPrestate() Option { + return func(c *config.Config) { + c.CannonAbsolutePreState = "/tmp/not-a-real-prestate.foo" + } +} + // FindMonorepoRoot finds the relative path to the monorepo root // Different tests might be nested in subdirectories of the op-e2e dir. func FindMonorepoRoot(t *testing.T) string { @@ -136,6 +148,13 @@ func WithCannon(t *testing.T, rollupCfg *rollup.Config, l2Genesis *core.Genesis) } } +func WithPermissioned(t *testing.T, rollupCfg *rollup.Config, l2Genesis *core.Genesis) Option { + return func(c *config.Config) { + c.TraceTypes = append(c.TraceTypes, types.TraceTypePermissioned) + applyCannonConfig(c, t, rollupCfg, l2Genesis) + } +} + func WithAlphabet() Option { return func(c *config.Config) { c.TraceTypes = append(c.TraceTypes, types.TraceTypeAlphabet) diff --git a/op-e2e/e2eutils/disputegame/helper.go b/op-e2e/e2eutils/disputegame/helper.go index 972314c4c..7651d0941 100644 --- a/op-e2e/e2eutils/disputegame/helper.go +++ b/op-e2e/e2eutils/disputegame/helper.go @@ -41,8 +41,9 @@ var ( ) const ( - cannonGameType uint32 = 0 - alphabetGameType uint32 = 255 + cannonGameType uint32 = 0 + permissionedGameType uint32 = 1 + alphabetGameType uint32 = 255 ) type GameCfg struct { @@ -95,13 +96,28 @@ type FactoryHelper struct { Factory *bindings.DisputeGameFactory } -func NewFactoryHelper(t *testing.T, ctx context.Context, system DisputeSystem) *FactoryHelper { +type FactoryCfg struct { + PrivKey *ecdsa.PrivateKey +} + +type FactoryOption func(c *FactoryCfg) + +func WithFactoryPrivKey(privKey *ecdsa.PrivateKey) FactoryOption { + return func(c *FactoryCfg) { + c.PrivKey = privKey + } +} + +func NewFactoryHelper(t *testing.T, ctx context.Context, system DisputeSystem, opts ...FactoryOption) *FactoryHelper { require := require.New(t) client := system.NodeClient("l1") chainID, err := client.ChainID(ctx) require.NoError(err) - privKey := TestKey - opts, err := bind.NewKeyedTransactorWithChainID(privKey, chainID) + factoryCfg := &FactoryCfg{PrivKey: TestKey} + for _, opt := range opts { + opt(factoryCfg) + } + txOpts, err := bind.NewKeyedTransactorWithChainID(factoryCfg.PrivKey, chainID) require.NoError(err) l1Deployments := system.L1Deployments() @@ -114,8 +130,8 @@ func NewFactoryHelper(t *testing.T, ctx context.Context, system DisputeSystem) * Require: require, System: system, Client: client, - Opts: opts, - PrivKey: privKey, + Opts: txOpts, + PrivKey: factoryCfg.PrivKey, Factory: factory, FactoryAddr: factoryAddr, } @@ -152,6 +168,14 @@ func (h *FactoryHelper) StartOutputCannonGameWithCorrectRoot(ctx context.Context } func (h *FactoryHelper) StartOutputCannonGame(ctx context.Context, l2Node string, l2BlockNumber uint64, rootClaim common.Hash, opts ...GameOpt) *OutputCannonGameHelper { + return h.startOutputCannonGameOfType(ctx, l2Node, l2BlockNumber, rootClaim, cannonGameType, opts...) +} + +func (h *FactoryHelper) StartPermissionedGame(ctx context.Context, l2Node string, l2BlockNumber uint64, rootClaim common.Hash, opts ...GameOpt) *OutputCannonGameHelper { + return h.startOutputCannonGameOfType(ctx, l2Node, l2BlockNumber, rootClaim, permissionedGameType, opts...) +} + +func (h *FactoryHelper) startOutputCannonGameOfType(ctx context.Context, l2Node string, l2BlockNumber uint64, rootClaim common.Hash, gameType uint32, opts ...GameOpt) *OutputCannonGameHelper { cfg := NewGameCfg(opts...) logger := testlog.Logger(h.T, log.LevelInfo).New("role", "OutputCannonGameHelper") rollupClient := h.System.RollupClient(l2Node) @@ -163,7 +187,7 @@ func (h *FactoryHelper) StartOutputCannonGame(ctx context.Context, l2Node string defer cancel() tx, err := transactions.PadGasEstimate(h.Opts, 2, func(opts *bind.TransactOpts) (*types.Transaction, error) { - return h.Factory.Create(opts, cannonGameType, rootClaim, extraData) + return h.Factory.Create(opts, gameType, rootClaim, extraData) }) h.Require.NoError(err, "create fault dispute game") rcpt, err := wait.ForReceiptOK(ctx, h.Client, tx.Hash()) diff --git a/op-e2e/faultproofs/permissioned_test.go b/op-e2e/faultproofs/permissioned_test.go new file mode 100644 index 000000000..09c4646fe --- /dev/null +++ b/op-e2e/faultproofs/permissioned_test.go @@ -0,0 +1,35 @@ +package faultproofs + +import ( + "context" + "testing" + + op_e2e "github.com/ethereum-optimism/optimism/op-e2e" + + "github.com/ethereum-optimism/optimism/op-e2e/e2eutils/challenger" + "github.com/ethereum-optimism/optimism/op-e2e/e2eutils/disputegame" + "github.com/ethereum/go-ethereum/common" +) + +func TestPermissionedGameType(t *testing.T) { + op_e2e.InitParallel(t, op_e2e.UsesCannon) + + ctx := context.Background() + sys, _ := StartFaultDisputeSystem(t) + t.Cleanup(sys.Close) + + gameFactory := disputegame.NewFactoryHelper(t, ctx, sys, disputegame.WithFactoryPrivKey(sys.Cfg.Secrets.Proposer)) + + game := gameFactory.StartPermissionedGame(ctx, "sequencer", 1, common.Hash{0x01, 0xaa}) + + // Start a challenger with both cannon and alphabet support + gameFactory.StartChallenger(ctx, "TowerDefense", + challenger.WithValidPrestateRequired(), + challenger.WithInvalidCannonPrestate(), + challenger.WithPermissioned(t, sys.RollupConfig, sys.L2GenesisCfg), + challenger.WithPrivKey(sys.Cfg.Secrets.Alice), + ) + + // Wait for the challenger to respond + game.RootClaim(ctx).WaitForCounterClaim(ctx) +} -- 2.23.0