Commit 39e9f1a4 authored by Adrian Sutton's avatar Adrian Sutton Committed by GitHub

op-e2e: Add action tests for challenger and stopping at the game timestamp (#13818)

* op-e2e: Add action test to confirm fault proof program stops when the agreed timestamp is already at the game timestamp

* op-e2e: Add action tests for the challenger super trace provider

Ensures that it generates super root/transition states that match the test expectations.
Update RootProvider interface to match the actual interface exposed by Supervisor

* op-e2e: Remove game inputs that are no longer required.
parent 05438612
...@@ -2,13 +2,18 @@ package interop ...@@ -2,13 +2,18 @@ package interop
import ( import (
"context" "context"
"fmt"
"log/slog" "log/slog"
"math/big"
"testing" "testing"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace/super"
challengerTypes "github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
fpHelpers "github.com/ethereum-optimism/optimism/op-e2e/actions/proofs/helpers" fpHelpers "github.com/ethereum-optimism/optimism/op-e2e/actions/proofs/helpers"
"github.com/ethereum-optimism/optimism/op-program/client/claim" "github.com/ethereum-optimism/optimism/op-program/client/claim"
"github.com/ethereum-optimism/optimism/op-program/client/interop" "github.com/ethereum-optimism/optimism/op-program/client/interop"
"github.com/ethereum-optimism/optimism/op-program/client/interop/types" "github.com/ethereum-optimism/optimism/op-program/client/interop/types"
"github.com/ethereum-optimism/optimism/op-service/eth"
"github.com/ethereum-optimism/optimism/op-service/testlog" "github.com/ethereum-optimism/optimism/op-service/testlog"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
...@@ -355,104 +360,129 @@ func TestInteropFaultProofs(gt *testing.T) { ...@@ -355,104 +360,129 @@ func TestInteropFaultProofs(gt *testing.T) {
tests := []*transitionTest{ tests := []*transitionTest{
{ {
name: "ClaimNoChange", name: "ClaimNoChange",
startTimestamp: startTimestamp,
agreedClaim: start.Marshal(), agreedClaim: start.Marshal(),
disputedClaim: start.Marshal(), disputedClaim: start.Marshal(),
disputedTraceIndex: 0,
expectValid: false, expectValid: false,
}, },
{ {
name: "ClaimDirectToNextTimestamp", name: "ClaimDirectToNextTimestamp",
startTimestamp: startTimestamp,
agreedClaim: start.Marshal(), agreedClaim: start.Marshal(),
disputedClaim: end.Marshal(), disputedClaim: end.Marshal(),
disputedTraceIndex: 0,
expectValid: false, expectValid: false,
}, },
{ {
name: "FirstChainOptimisticBlock", name: "FirstChainOptimisticBlock",
startTimestamp: startTimestamp,
agreedClaim: start.Marshal(), agreedClaim: start.Marshal(),
disputedClaim: step1Expected, disputedClaim: step1Expected,
disputedTraceIndex: 0,
expectValid: true, expectValid: true,
}, },
{ {
name: "SecondChainOptimisticBlock", name: "SecondChainOptimisticBlock",
startTimestamp: startTimestamp,
agreedClaim: step1Expected, agreedClaim: step1Expected,
disputedClaim: step2Expected, disputedClaim: step2Expected,
disputedTraceIndex: 1,
expectValid: true, expectValid: true,
}, },
{ {
name: "FirstPaddingStep", name: "FirstPaddingStep",
startTimestamp: startTimestamp,
agreedClaim: step2Expected, agreedClaim: step2Expected,
disputedClaim: paddingStep(3), disputedClaim: paddingStep(3),
disputedTraceIndex: 2,
expectValid: true, expectValid: true,
}, },
{ {
name: "SecondPaddingStep", name: "SecondPaddingStep",
startTimestamp: startTimestamp,
agreedClaim: paddingStep(3), agreedClaim: paddingStep(3),
disputedClaim: paddingStep(4), disputedClaim: paddingStep(4),
disputedTraceIndex: 3,
expectValid: true, expectValid: true,
}, },
{ {
name: "LastPaddingStep", name: "LastPaddingStep",
startTimestamp: startTimestamp,
agreedClaim: paddingStep(1022), agreedClaim: paddingStep(1022),
disputedClaim: paddingStep(1023), disputedClaim: paddingStep(1023),
disputedTraceIndex: 1022,
expectValid: true, expectValid: true,
}, },
{ {
name: "Consolidate", name: "Consolidate-AllValid",
startTimestamp: startTimestamp,
agreedClaim: paddingStep(1023), agreedClaim: paddingStep(1023),
disputedClaim: end.Marshal(), disputedClaim: end.Marshal(),
disputedTraceIndex: 1023,
expectValid: true,
},
{
name: "Consolidate-ReplaceInvalidBlock",
// Will need to generate an invalid block before this can be enabled
skipProgram: true,
skipChallenger: true,
},
{
name: "Consolidate-ReplaceBlockInvalidatedByFirstInvalidatedBlock",
// Will need to generate an invalid block before this can be enabled
// Check that if a block B depends on a log in block A, and block A is found to have an invalid message
// that block B is also replaced with a deposit only block because A no longer contains the log it needs
skipProgram: true,
skipChallenger: true,
},
{
name: "AlreadyAtClaimedTimestamp",
agreedClaim: end.Marshal(),
disputedClaim: end.Marshal(),
disputedTraceIndex: 5000,
expectValid: true, expectValid: true,
}, },
{ {
name: "FirstChainReachesL1Head", name: "FirstChainReachesL1Head",
startTimestamp: startTimestamp,
agreedClaim: start.Marshal(), agreedClaim: start.Marshal(),
disputedClaim: interop.InvalidTransition, disputedClaim: interop.InvalidTransition,
disputedTraceIndex: 0,
// The derivation reaches the L1 head before the next block can be created // The derivation reaches the L1 head before the next block can be created
l1Head: actors.L1Miner.L1Chain().Genesis().Hash(), l1Head: actors.L1Miner.L1Chain().Genesis().Hash(),
expectValid: true, expectValid: true,
skipChallenger: true, // Challenger doesn't yet check if blocks were safe
}, },
{ {
name: "SecondChainReachesL1Head", name: "SecondChainReachesL1Head",
startTimestamp: startTimestamp,
agreedClaim: step1Expected, agreedClaim: step1Expected,
disputedClaim: interop.InvalidTransition, disputedClaim: interop.InvalidTransition,
disputedTraceIndex: 1,
// The derivation reaches the L1 head before the next block can be created // The derivation reaches the L1 head before the next block can be created
l1Head: actors.L1Miner.L1Chain().Genesis().Hash(), l1Head: actors.L1Miner.L1Chain().Genesis().Hash(),
expectValid: true, expectValid: true,
skipChallenger: true, // Challenger doesn't yet check if blocks were safe
}, },
{ {
name: "SuperRootInvalidIfUnsupportedByL1Data", name: "SuperRootInvalidIfUnsupportedByL1Data",
startTimestamp: startTimestamp,
agreedClaim: step1Expected, agreedClaim: step1Expected,
disputedClaim: step2Expected, disputedClaim: step2Expected,
disputedTraceIndex: 1,
// The derivation reaches the L1 head before the next block can be created // The derivation reaches the L1 head before the next block can be created
l1Head: actors.L1Miner.L1Chain().Genesis().Hash(), l1Head: actors.L1Miner.L1Chain().Genesis().Hash(),
expectValid: false, expectValid: false,
skipChallenger: true, // Challenger doesn't yet check if blocks were safe
}, },
{ {
name: "FromInvalidTransitionHash", name: "FromInvalidTransitionHash",
startTimestamp: startTimestamp,
agreedClaim: interop.InvalidTransition, agreedClaim: interop.InvalidTransition,
disputedClaim: interop.InvalidTransition, disputedClaim: interop.InvalidTransition,
disputedTraceIndex: 2,
// The derivation reaches the L1 head before the next block can be created // The derivation reaches the L1 head before the next block can be created
l1Head: actors.L1Miner.L1Chain().Genesis().Hash(), l1Head: actors.L1Miner.L1Chain().Genesis().Hash(),
expectValid: true, expectValid: true,
skipChallenger: true, // Challenger doesn't yet check if blocks were safe
}, },
} }
for _, test := range tests { for _, test := range tests {
test := test test := test
gt.Run(test.name, func(gt *testing.T) { gt.Run(fmt.Sprintf("%s-fpp", test.name), func(gt *testing.T) {
t := helpers.NewDefaultTesting(gt) t := helpers.NewDefaultTesting(gt)
if test.skip { if test.skipProgram {
t.Skip("Not yet implemented") t.Skip("Not yet implemented")
return return
} }
...@@ -474,6 +504,42 @@ func TestInteropFaultProofs(gt *testing.T) { ...@@ -474,6 +504,42 @@ func TestInteropFaultProofs(gt *testing.T) {
fpHelpers.WithL1Head(l1Head), fpHelpers.WithL1Head(l1Head),
) )
}) })
gt.Run(fmt.Sprintf("%s-challenger", test.name), func(gt *testing.T) {
t := helpers.NewDefaultTesting(gt)
if test.skipChallenger {
t.Skip("Not yet implemented")
return
}
logger := testlog.Logger(t, slog.LevelInfo)
prestateProvider := super.NewSuperRootPrestateProvider(&actors.Supervisor.QueryFrontend, startTimestamp)
var l1Head eth.BlockID
if test.l1Head == (common.Hash{}) {
l1Head = eth.ToBlockID(eth.HeaderBlockInfo(actors.L1Miner.L1Chain().CurrentBlock()))
} else {
l1Head = eth.ToBlockID(actors.L1Miner.L1Chain().GetBlockByHash(test.l1Head))
}
gameDepth := challengerTypes.Depth(30)
provider := super.NewSuperTraceProvider(logger, prestateProvider, &actors.Supervisor.QueryFrontend, l1Head, gameDepth, startTimestamp, endTimestamp)
var agreedPrestate []byte
if test.disputedTraceIndex > 0 {
agreedPrestate, err = provider.GetPreimageBytes(ctx, challengerTypes.NewPosition(gameDepth, big.NewInt(test.disputedTraceIndex-1)))
require.NoError(t, err)
} else {
superRoot, err := provider.AbsolutePreState(ctx)
require.NoError(t, err)
agreedPrestate = superRoot.Marshal()
}
require.Equal(t, test.agreedClaim, agreedPrestate)
disputedClaim, err := provider.GetPreimageBytes(ctx, challengerTypes.NewPosition(gameDepth, big.NewInt(test.disputedTraceIndex)))
require.NoError(t, err)
if test.expectValid {
require.Equal(t, test.disputedClaim, disputedClaim, "Claim is correct so should match challenger's opinion")
} else {
require.NotEqual(t, test.disputedClaim, disputedClaim, "Claim is incorrect so should not match challenger's opinion")
}
})
} }
} }
...@@ -485,10 +551,6 @@ func WithInteropEnabled(actors *InteropActors, agreedPrestate []byte, disputedCl ...@@ -485,10 +551,6 @@ func WithInteropEnabled(actors *InteropActors, agreedPrestate []byte, disputedCl
f.L2Claim = disputedClaim f.L2Claim = disputedClaim
f.L2BlockNumber = claimTimestamp f.L2BlockNumber = claimTimestamp
// TODO: Remove these once hints all specify the L2 chain ID
f.L2ChainID = actors.ChainA.ChainID.ToBig().Uint64()
f.L2Head = actors.ChainA.SequencerEngine.L2Chain().CurrentHeader().ParentHash
for _, chain := range []*Chain{actors.ChainA, actors.ChainB} { for _, chain := range []*Chain{actors.ChainA, actors.ChainB} {
f.L2Sources = append(f.L2Sources, &fpHelpers.FaultProofProgramL2Source{ f.L2Sources = append(f.L2Sources, &fpHelpers.FaultProofProgramL2Source{
Node: chain.Sequencer.L2Verifier, Node: chain.Sequencer.L2Verifier,
...@@ -501,10 +563,11 @@ func WithInteropEnabled(actors *InteropActors, agreedPrestate []byte, disputedCl ...@@ -501,10 +563,11 @@ func WithInteropEnabled(actors *InteropActors, agreedPrestate []byte, disputedCl
type transitionTest struct { type transitionTest struct {
name string name string
startTimestamp uint64
agreedClaim []byte agreedClaim []byte
disputedClaim []byte disputedClaim []byte
disputedTraceIndex int64
l1Head common.Hash // Defaults to current L1 head if not set l1Head common.Hash // Defaults to current L1 head if not set
expectValid bool expectValid bool
skip bool skipProgram bool
skipChallenger bool
} }
...@@ -59,7 +59,7 @@ func (ib *InteropAPI) OutputV0AtTimestamp(ctx context.Context, timestamp uint64) ...@@ -59,7 +59,7 @@ func (ib *InteropAPI) OutputV0AtTimestamp(ctx context.Context, timestamp uint64)
return ib.backend.OutputV0AtTimestamp(ctx, timestamp) return ib.backend.OutputV0AtTimestamp(ctx, timestamp)
} }
func (ib *InteropAPI) PendingOutputV0ATTimestamp(ctx context.Context, timestamp uint64) (*eth.OutputV0, error) { func (ib *InteropAPI) PendingOutputV0AtTimestamp(ctx context.Context, timestamp uint64) (*eth.OutputV0, error) {
return ib.backend.PendingOutputV0AtTimestamp(ctx, timestamp) return ib.backend.PendingOutputV0AtTimestamp(ctx, timestamp)
} }
......
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