From 23e1ba6dc550b5838bbd60debef144405b87961e Mon Sep 17 00:00:00 2001 From: Po Date: Tue, 29 Oct 2024 20:44:25 +0800 Subject: [PATCH] op-challenger: implement NewSplitProviderSelector2 for pre/post outputRoot and findAncestorProofAtDepth2 for stepProof --- .../game/fault/test/claim_builder.go | 10 +- op-challenger/game/fault/test/game_builder.go | 8 +- op-challenger/game/fault/trace/access2.go | 124 ++++++++- op-challenger/game/fault/trace/access_test.go | 246 +++++++++++++++++ op-challenger/game/fault/trace/split/split.go | 74 +---- .../game/fault/trace/split/split2.go | 101 +++++++ .../game/fault/trace/split/split_test.go | 259 +++++++++++++++--- op-challenger/game/fault/types/game.go | 43 +-- op-challenger/game/fault/types/types.go | 12 + 9 files changed, 742 insertions(+), 135 deletions(-) create mode 100644 op-challenger/game/fault/trace/split/split2.go diff --git a/op-challenger/game/fault/test/claim_builder.go b/op-challenger/game/fault/test/claim_builder.go index e85949eefcd3..b68cd2fa5c21 100644 --- a/op-challenger/game/fault/test/claim_builder.go +++ b/op-challenger/game/fault/test/claim_builder.go @@ -22,6 +22,7 @@ type claimCfg struct { parentIdx int clockTimestamp time.Time clockDuration time.Duration + branch uint64 } func newClaimCfg(opts ...ClaimOpt) *claimCfg { @@ -75,6 +76,12 @@ func WithClock(timestamp time.Time, duration time.Duration) ClaimOpt { }) } +func WithBranch(branch uint64) ClaimOpt { + return claimOptFn(func(cfg *claimCfg) { + cfg.branch = branch + }) +} + // ClaimBuilder is a test utility to enable creating claims in a wide range of situations type ClaimBuilder struct { require *require.Assertions @@ -141,6 +148,7 @@ func (c *ClaimBuilder) claim(pos types.Position, opts ...ClaimOpt) types.Claim { Duration: cfg.clockDuration, Timestamp: cfg.clockTimestamp, }, + AttackBranch: cfg.branch, } if cfg.claimant != (common.Address{}) { claim.Claimant = cfg.claimant @@ -197,5 +205,5 @@ func GetClaimsHash(values []common.Hash) common.Hash { func (c *ClaimBuilder) AttackClaim2(claim types.Claim, subClaims []common.Hash, nbits uint64, branch uint64, opts ...ClaimOpt) types.Claim { pos := claim.Position.Attack2(nbits, branch) topClaim := GetClaimsHash(subClaims) - return c.claim(pos, append([]ClaimOpt{WithParent(claim), WithValue(topClaim)}, opts...)...) + return c.claim(pos, append([]ClaimOpt{WithParent(claim), WithValue(topClaim), WithBranch(branch)}, opts...)...) } diff --git a/op-challenger/game/fault/test/game_builder.go b/op-challenger/game/fault/test/game_builder.go index 16a9779f2744..d0f6269ab973 100644 --- a/op-challenger/game/fault/test/game_builder.go +++ b/op-challenger/game/fault/test/game_builder.go @@ -29,10 +29,10 @@ func (c *ClaimBuilder) GameBuilder(rootOpts ...ClaimOpt) *GameBuilder { } } -func (c *ClaimBuilder) GameBuilder2(nary int64, rootOpts ...ClaimOpt) *GameBuilder { +func (c *ClaimBuilder) GameBuilder2(nary int64, splitDepth types.Depth, rootOpts ...ClaimOpt) *GameBuilder { return &GameBuilder{ builder: c, - Game: types.NewGameState2([]types.Claim{c.CreateRootClaim(rootOpts...)}, c.maxDepth, nary, make(map[types.ClaimID][]common.Hash)), + Game: types.NewGameState2([]types.Claim{c.CreateRootClaim(rootOpts...)}, c.maxDepth, splitDepth, nary, make(map[types.ClaimID][]common.Hash)), } } @@ -82,7 +82,9 @@ func (s *GameBuilderSeq) addClaimAndSubClaimsToGame(claim *types.Claim, subClaim claims := append(s.gameBuilder.Game.Claims(), *claim) allSubClaims := s.gameBuilder.Game.SubClaims() allSubClaims[claim.ID()] = subClaims - s.gameBuilder.Game = types.NewGameState2(claims, s.builder.maxDepth, s.gameBuilder.Game.Nary().Int64(), allSubClaims) + splitDepth := s.gameBuilder.Game.SplitDepth() + nary := s.gameBuilder.Game.Nary().Int64() + s.gameBuilder.Game = types.NewGameState2(claims, s.builder.maxDepth, splitDepth, nary, allSubClaims) } func (s *GameBuilderSeq) Attack(opts ...ClaimOpt) *GameBuilderSeq { diff --git a/op-challenger/game/fault/trace/access2.go b/op-challenger/game/fault/trace/access2.go index 9537bdbaff95..f2d6891df0e7 100644 --- a/op-challenger/game/fault/trace/access2.go +++ b/op-challenger/game/fault/trace/access2.go @@ -2,18 +2,138 @@ package trace import ( "context" + "fmt" + "math/big" "github.com/ethereum-optimism/optimism/op-challenger/game/fault/types" ) +// Get the traceIdx's accestor claims hash and its merkel proof in subclaims. traceIdx can be ref's traceIdx ±1. +// Params: +// - relativeTraceIdx: the trace index relative to splitDepth +func findAncestorProofAtDepth2(ctx context.Context, provider types.TraceProvider, game types.Game, ref types.Claim, relativeTraceIdx *big.Int) (types.DAItem, error) { + maxTraceDepth := game.MaxDepth() - game.SplitDepth() + // Find the ancestor claim at the (splitDepth + 1) level. + splitLeaf, err := FindAncestorAtDepth(game, ref, types.Depth(game.SplitDepth()+game.NBits())) + if err != nil { + return types.DAItem{}, err + } + // If traceIdx equals -1, the absolute prestate is returned. + if relativeTraceIdx.Cmp(big.NewInt(-1)) == 0 { + absolutePresate, err := provider.AbsolutePreStateCommitment(ctx) + if err != nil { + return types.DAItem{}, fmt.Errorf("failed to get absolutePrestate: %w", err) + } + return types.DAItem{ + DaType: types.CallDataType, + DataHash: absolutePresate.Bytes(), + Proof: []byte{}, + }, nil + } + + // If traceIdx is the right most branch, the root Claim is returned. + if new(big.Int).Add(relativeTraceIdx, big.NewInt(1)).Cmp(new(big.Int).Lsh(big.NewInt(1), uint(maxTraceDepth-game.NBits()))) == 0 { + return types.DAItem{ + DaType: types.CallDataType, + DataHash: splitLeaf.Value.Bytes(), + Proof: []byte{}, + }, nil + } + + ancestor := ref + relativeAncestorPos, err := ancestor.RelativeToAncestorAtDepth(game.SplitDepth()) + if err != nil { + return types.DAItem{}, err + } + minTraceIndex := relativeAncestorPos.TraceIndex(maxTraceDepth) + offset := new(big.Int).Lsh(big.NewInt(int64(game.MaxAttackBranch())-1), uint(maxTraceDepth-relativeAncestorPos.Depth())) + maxTraceIndex := new(big.Int).Add(minTraceIndex, offset) + // If Ancestor's left most branch traceIdx <= traceId <= Ancestor's rightmost branch traceIdx, we do nothing; + // otherwise, we should trace the correct ancestor + for minTraceIndex.Cmp(relativeTraceIdx) == 1 || maxTraceIndex.Cmp(relativeTraceIdx) == -1 { + parent, err := game.GetParent(ancestor) + if err != nil { + return types.DAItem{}, fmt.Errorf("failed to get ancestor of claim %v: %w", ancestor.ContractIndex, err) + } + + ancestor = parent + relativeAncestorPos, err = ancestor.RelativeToAncestorAtDepth(game.SplitDepth()) + if err != nil { + return types.DAItem{}, err + } + minTraceIndex = relativeAncestorPos.TraceIndex(maxTraceDepth) + offset = new(big.Int).Lsh(big.NewInt(int64(game.MaxAttackBranch())-1), uint(maxTraceDepth-relativeAncestorPos.Depth())) + maxTraceIndex = new(big.Int).Add(minTraceIndex, offset) + } + + branch := new(big.Int).Div(new(big.Int).Sub(relativeTraceIdx, minTraceIndex), new(big.Int).Lsh(big.NewInt(1), uint(maxTraceDepth-relativeAncestorPos.Depth()))).Int64() + subclaims, err := game.GetSubClaims(ancestor) + if err != nil { + return types.DAItem{}, fmt.Errorf("failed to get subclaims of claim %v: %w", ref.ContractIndex, err) + } + + ancestorClaim := subclaims[branch] + merkleProof := make([]byte, 0) + for i := int64(0); i < branch; i++ { + merkleProof = append(merkleProof, subclaims[i].Bytes()...) + } + + for i := branch + 1; i < int64(game.MaxAttackBranch()); i++ { + merkleProof = append(merkleProof, subclaims[i].Bytes()...) + } + + return types.DAItem{ + DaType: types.CallDataType, + DataHash: ancestorClaim.Bytes(), + Proof: merkleProof, + }, nil +} + +// Params: +// - ref: the attacked claim +// - pos: next attacking position (global) for the ref claim. func (t *Accessor) GetStepData2(ctx context.Context, game types.Game, ref types.Claim, pos types.Position) (prestate []byte, proofData []byte, preimageData *types.PreimageOracleData, err error) { + // Get oracle data + // prestate, proofData, preimageData, err = t.GetStepData(ctx, game, ref, pos) provider, err := t.selector(ctx, game, ref, pos) if err != nil { return nil, nil, nil, err } - parentPos, err := game.GetParent(ref) + prestate, proofData, preimageData, err = provider.GetStepData2(ctx, pos, pos) if err != nil { return nil, nil, nil, err } - return provider.GetStepData2(ctx, pos, parentPos.Position) + // Get stepProof for stepV2 + relativePos, err := pos.RelativeToAncestorAtDepth(game.SplitDepth()) + if err != nil { + return nil, nil, nil, err + } + postTraceIdx := relativePos.TraceIndex(game.MaxDepth()) + preTraceIdx := new(big.Int).Sub(postTraceIdx, big.NewInt(1)) + preStateDaItem, err := findAncestorProofAtDepth2(ctx, provider, game, ref, preTraceIdx) + if err != nil { + return nil, nil, nil, fmt.Errorf("failed to get postStateDaItem at trace index %v: %w", preTraceIdx, err) + } + postStateDaItem, err := findAncestorProofAtDepth2(ctx, provider, game, ref, postTraceIdx) + if err != nil { + return nil, nil, nil, fmt.Errorf("failed to get postStateDaItem at trace index %v: %w", postTraceIdx, err) + } + StepProof := types.StepProofData{ + Prestate: preStateDaItem, + PostState: postStateDaItem, + } + + preimageData.StepProof = StepProof + return prestate, proofData, preimageData, nil +} + +func FindAncestorAtDepth(game types.Game, claim types.Claim, depth types.Depth) (types.Claim, error) { + for claim.Depth() > depth { + parent, err := game.GetParent(claim) + if err != nil { + return types.Claim{}, fmt.Errorf("failed to find ancestor at depth %v: %w", depth, err) + } + claim = parent + } + return claim, nil } diff --git a/op-challenger/game/fault/trace/access_test.go b/op-challenger/game/fault/trace/access_test.go index 04c3ea3e6e7c..7ba982e00c42 100644 --- a/op-challenger/game/fault/trace/access_test.go +++ b/op-challenger/game/fault/trace/access_test.go @@ -10,6 +10,7 @@ import ( "github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace/alphabet" "github.com/ethereum-optimism/optimism/op-challenger/game/fault/types" "github.com/ethereum-optimism/optimism/op-service/eth" + "github.com/ethereum/go-ethereum/common" "github.com/stretchr/testify/require" ) @@ -88,6 +89,251 @@ func TestAccessor_UsesSelector(t *testing.T) { }) } +func TestFindAncestorProofAtDepth2(t *testing.T) { + nbits := uint64(2) + nary := int64(1) << nbits + maxGameDepth := types.Depth(8) + splitDepth := types.Depth(2) + claimBuilder := test.NewAlphabetClaimBuilder(t, big.NewInt(0), maxGameDepth) + traceDepth := maxGameDepth - splitDepth - types.Depth(nbits) + ctx := context.Background() + provider := test.NewAlphabetWithProofProvider(t, big.NewInt(0), traceDepth, nil) + + { // Attack the left most claim + depth8Branch := uint64(0) + stepBranch := int64(0) + + // output roots + gameBuilder := claimBuilder.GameBuilder2(nary, splitDepth) + seq := gameBuilder.Seq() + claimBuilder.CreateRootClaim(test.WithValue(common.Hash{0x00})) + subClaimsDep2 := []common.Hash{{0x01}, {0x02}, {0x03}} // claim at splitDepth + seq = seq.Attack2(subClaimsDep2[:], nbits, 0) + // traces + subClaimsDep4 := []common.Hash{{0x04}} + seq = seq.Attack2(subClaimsDep4[:], nbits, 0) + subClaimsDep6 := []common.Hash{{0x61}, {0x62}, {0x63}} + seq = seq.Attack2(subClaimsDep6[:], nbits, 0) + subClaimsDep8 := []common.Hash{{0x81}, {0x82}, {0x83}} + seq.Attack2(subClaimsDep8[:], nbits, depth8Branch) + + game := gameBuilder.Game + ref := game.Claims()[4] + pos := types.NewPositionFromGIndex(big.NewInt(ref.ToGIndex().Int64() + stepBranch)) + traceRootDepth := game.RootDepth() + relativePos, err := pos.RelativeToAncestorAtDepth(traceRootDepth) + require.NoError(t, err) + postTraceIdx := relativePos.TraceIndex(traceDepth) + preTraceIdx := new(big.Int).Sub(postTraceIdx, big.NewInt(1)) + + absolutePre, err := provider.AbsolutePreStateCommitment(ctx) + require.NoError(t, err) + expectedPre := types.DAItem{ + DaType: types.CallDataType, + DataHash: absolutePre.Bytes(), + Proof: []byte{}, + } + preStateDaItem, err := findAncestorProofAtDepth2(ctx, provider, game, ref, preTraceIdx) + require.NoError(t, err) + require.Equal(t, expectedPre, preStateDaItem) + + expectedPost := types.DAItem{ + DaType: types.CallDataType, + DataHash: subClaimsDep8[0][:], + Proof: append(subClaimsDep8[1][:], subClaimsDep8[2][:]...), + } + postStateDaItem, err := findAncestorProofAtDepth2(ctx, provider, game, ref, postTraceIdx) + require.NoError(t, err) + require.Equal(t, expectedPost, postStateDaItem) + } + + { // Attack the left claim + depth8Branch := uint64(1) + stepBranch := int64(0) + + // output roots + gameBuilder := claimBuilder.GameBuilder2(nary, splitDepth) + seq := gameBuilder.Seq() + claimBuilder.CreateRootClaim(test.WithValue(common.Hash{0x00})) + subClaimsDep2 := []common.Hash{{0x01}, {0x02}, {0x03}} // claim at splitDepth + seq = seq.Attack2(subClaimsDep2[:], nbits, 0) + // traces + subClaimsDep4 := []common.Hash{{0x04}} + seq = seq.Attack2(subClaimsDep4[:], nbits, 0) + subClaimsDep6 := []common.Hash{{0x61}, {0x62}, {0x63}} + seq = seq.Attack2(subClaimsDep6[:], nbits, 0) + subClaimsDep8 := []common.Hash{{0x81}, {0x82}, {0x83}} + seq.Attack2(subClaimsDep8[:], nbits, depth8Branch) + + game := gameBuilder.Game + ref := game.Claims()[4] + pos := types.NewPositionFromGIndex(big.NewInt(ref.ToGIndex().Int64() + stepBranch)) + traceRootDepth := game.RootDepth() + relativePos, err := pos.RelativeToAncestorAtDepth(traceRootDepth) + require.NoError(t, err) + postTraceIdx := relativePos.TraceIndex(traceDepth) + preTraceIdx := new(big.Int).Sub(postTraceIdx, big.NewInt(1)) + + expectedPre := types.DAItem{ + DaType: types.CallDataType, + DataHash: subClaimsDep6[0][:], + Proof: append(subClaimsDep6[1][:], subClaimsDep6[2][:]...), + } + preStateDaItem, err := findAncestorProofAtDepth2(ctx, provider, game, ref, preTraceIdx) + require.NoError(t, err) + require.Equal(t, expectedPre, preStateDaItem) + + expectedPost := types.DAItem{ + DaType: types.CallDataType, + DataHash: subClaimsDep8[0][:], + Proof: append(subClaimsDep8[1][:], subClaimsDep8[2][:]...), + } + postStateDaItem, err := findAncestorProofAtDepth2(ctx, provider, game, ref, postTraceIdx) + require.NoError(t, err) + require.Equal(t, expectedPost, postStateDaItem) + } + + { // Attack the mid claim + depth8Branch := uint64(1) + stepBranch := int64(1) + + // output roots + gameBuilder := claimBuilder.GameBuilder2(nary, splitDepth) + seq := gameBuilder.Seq() + claimBuilder.CreateRootClaim(test.WithValue(common.Hash{0x00})) + subClaimsDep2 := []common.Hash{{0x01}, {0x02}, {0x03}} // claim at splitDepth + seq = seq.Attack2(subClaimsDep2[:], nbits, 0) + // traces + subClaimsDep4 := []common.Hash{{0x04}} + seq = seq.Attack2(subClaimsDep4[:], nbits, 0) + subClaimsDep6 := []common.Hash{{0x61}, {0x62}, {0x63}} + seq = seq.Attack2(subClaimsDep6[:], nbits, 0) + subClaimsDep8 := []common.Hash{{0x81}, {0x82}, {0x83}} + seq.Attack2(subClaimsDep8[:], nbits, depth8Branch) + + game := gameBuilder.Game + ref := game.Claims()[4] + pos := types.NewPositionFromGIndex(big.NewInt(ref.ToGIndex().Int64() + stepBranch)) + traceRootDepth := game.RootDepth() + relativePos, err := pos.RelativeToAncestorAtDepth(traceRootDepth) + require.NoError(t, err) + postTraceIdx := relativePos.TraceIndex(traceDepth) + preTraceIdx := new(big.Int).Sub(postTraceIdx, big.NewInt(1)) + + expectedPre := types.DAItem{ + DaType: types.CallDataType, + DataHash: subClaimsDep8[0][:], + Proof: append(subClaimsDep8[1][:], subClaimsDep8[2][:]...), + } + preStateDaItem, err := findAncestorProofAtDepth2(ctx, provider, game, ref, preTraceIdx) + require.NoError(t, err) + require.Equal(t, expectedPre, preStateDaItem) + + expectedPost := types.DAItem{ + DaType: types.CallDataType, + DataHash: subClaimsDep8[1][:], + Proof: append(subClaimsDep8[0][:], subClaimsDep8[2][:]...), + } + postStateDaItem, err := findAncestorProofAtDepth2(ctx, provider, game, ref, postTraceIdx) + require.NoError(t, err) + require.Equal(t, expectedPost, postStateDaItem) + } + + { // Attack the right claim + depth8Branch := uint64(1) + stepBranch := int64(3) + + // output roots + gameBuilder := claimBuilder.GameBuilder2(nary, splitDepth) + seq := gameBuilder.Seq() + claimBuilder.CreateRootClaim(test.WithValue(common.Hash{0x00})) + subClaimsDep2 := []common.Hash{{0x01}, {0x02}, {0x03}} // claim at splitDepth + seq = seq.Attack2(subClaimsDep2[:], nbits, 0) + // traces + subClaimsDep4 := []common.Hash{{0x04}} + seq = seq.Attack2(subClaimsDep4[:], nbits, 0) + subClaimsDep6 := []common.Hash{{0x61}, {0x62}, {0x63}} + seq = seq.Attack2(subClaimsDep6[:], nbits, 0) + subClaimsDep8 := []common.Hash{{0x81}, {0x82}, {0x83}} + seq.Attack2(subClaimsDep8[:], nbits, depth8Branch) + + game := gameBuilder.Game + ref := game.Claims()[4] + pos := types.NewPositionFromGIndex(big.NewInt(ref.ToGIndex().Int64() + stepBranch)) + traceRootDepth := game.RootDepth() + relativePos, err := pos.RelativeToAncestorAtDepth(traceRootDepth) + require.NoError(t, err) + postTraceIdx := relativePos.TraceIndex(traceDepth) + preTraceIdx := new(big.Int).Sub(postTraceIdx, big.NewInt(1)) + + expectedPre := types.DAItem{ + DaType: types.CallDataType, + DataHash: subClaimsDep8[2][:], + Proof: append(subClaimsDep8[0][:], subClaimsDep8[1][:]...), + } + preStateDaItem, err := findAncestorProofAtDepth2(ctx, provider, game, ref, preTraceIdx) + require.NoError(t, err) + require.Equal(t, expectedPre, preStateDaItem) + + expectedPost := types.DAItem{ + DaType: types.CallDataType, + DataHash: subClaimsDep6[1][:], + Proof: append(subClaimsDep6[0][:], subClaimsDep6[2][:]...), + } + postStateDaItem, err := findAncestorProofAtDepth2(ctx, provider, game, ref, postTraceIdx) + require.NoError(t, err) + require.Equal(t, expectedPost, postStateDaItem) + } + + { // Attack the right most claim + depth8Branch := uint64(3) + stepBranch := int64(3) + + // output roots + gameBuilder := claimBuilder.GameBuilder2(nary, splitDepth) + seq := gameBuilder.Seq() + claimBuilder.CreateRootClaim(test.WithValue(common.Hash{0x00})) + subClaimsDep2 := []common.Hash{{0x01}, {0x02}, {0x03}} // claim at splitDepth + seq = seq.Attack2(subClaimsDep2[:], nbits, 0) + // traces + subClaimsDep4 := []common.Hash{{0x04}} + seq = seq.Attack2(subClaimsDep4[:], nbits, 0) + subClaimsDep6 := []common.Hash{{0x61}, {0x62}, {0x63}} + seq = seq.Attack2(subClaimsDep6[:], nbits, 0) + subClaimsDep8 := []common.Hash{{0x81}, {0x82}, {0x83}} + seq.Attack2(subClaimsDep8[:], nbits, depth8Branch) + + game := gameBuilder.Game + ref := game.Claims()[4] + pos := types.NewPositionFromGIndex(big.NewInt(ref.ToGIndex().Int64() + stepBranch)) + traceRootDepth := game.RootDepth() + relativePos, err := pos.RelativeToAncestorAtDepth(traceRootDepth) + require.NoError(t, err) + postTraceIdx := relativePos.TraceIndex(traceDepth) + preTraceIdx := new(big.Int).Sub(postTraceIdx, big.NewInt(1)) + + expectedPre := types.DAItem{ + DaType: types.CallDataType, + DataHash: subClaimsDep8[2][:], + Proof: append(subClaimsDep8[0][:], subClaimsDep8[1][:]...), + } + preStateDaItem, err := findAncestorProofAtDepth2(ctx, provider, game, ref, preTraceIdx) + require.NoError(t, err) + require.Equal(t, expectedPre, preStateDaItem) + + splitLeaf := game.Claims()[2] + expectedPost := types.DAItem{ + DaType: types.CallDataType, + DataHash: splitLeaf.Value[:], + Proof: []byte{}, + } + postStateDaItem, err := findAncestorProofAtDepth2(ctx, provider, game, ref, postTraceIdx) + require.NoError(t, err) + require.Equal(t, expectedPost, postStateDaItem) + } + +} + type ChallengeTraceProvider struct { types.TraceProvider } diff --git a/op-challenger/game/fault/trace/split/split.go b/op-challenger/game/fault/trace/split/split.go index 92a7f30d551f..9c4f7370721b 100644 --- a/op-challenger/game/fault/trace/split/split.go +++ b/op-challenger/game/fault/trace/split/split.go @@ -26,7 +26,7 @@ func NewSplitProviderSelector(topProvider types.TraceProvider, topDepth types.De } // Find the ancestor claim at the leaf level for the top game. - topLeaf, err := findAncestorAtDepth(game, ref, topDepth) + topLeaf, err := trace.FindAncestorAtDepth(game, ref, topDepth) if err != nil { return nil, err } @@ -68,17 +68,6 @@ func NewSplitProviderSelector(topProvider types.TraceProvider, topDepth types.De } } -func findAncestorAtDepth(game types.Game, claim types.Claim, depth types.Depth) (types.Claim, error) { - for claim.Depth() > depth { - parent, err := game.GetParent(claim) - if err != nil { - return types.Claim{}, fmt.Errorf("failed to find ancestor at depth %v: %w", depth, err) - } - claim = parent - } - return claim, nil -} - func findAncestorWithTraceIndex(game types.Game, ref types.Claim, depth types.Depth, traceIdx *big.Int) (types.Claim, error) { candidate := ref for candidate.TraceIndex(depth).Cmp(traceIdx) != 0 { @@ -90,64 +79,3 @@ func findAncestorWithTraceIndex(game types.Game, ref types.Claim, depth types.De } return candidate, nil } - -// Get the traceIdx's accestor claims hash and its merkel proof in subclaims. traceIdx can be ref's traceIdx ±1. -func findAncestorProofAtDepth2(game types.Game, ref types.Claim, traceIdx *big.Int) (types.DAItem, error) { - // If traceIdx equals -1, the absolute prestate is returned. - if traceIdx.Cmp(big.NewInt(-1)) == 0 { - return types.DAItem{ - DaType: types.CallDataType, - DataHash: game.AbsolutePrestate().Value.Bytes(), - Proof: []byte{}, - }, fmt.Errorf("absolute prestate is not implemented") - } - - // If traceIdx is the right most branch, the root Claim is returned. - if new(big.Int).Add(traceIdx, big.NewInt(1)).Cmp(new(big.Int).Lsh(big.NewInt(1), uint(game.MaxDepth()))) == 0 { - return types.DAItem{ - DaType: types.CallDataType, - DataHash: game.RootClaim().Value.Bytes(), - Proof: []byte{}, - }, nil - } - - ancestor := ref - minTraceIndex := ancestor.TraceIndex(game.MaxDepth()) - offset := new(big.Int).Lsh(big.NewInt(int64(game.MaxAttackBranch())-1), uint(game.MaxDepth()-ancestor.Depth())) - maxTraceIndex := new(big.Int).Add(minTraceIndex, offset) - // If Ancestor's left most branch traceIdx <= traceId <= Ancestor's rightmost branch traceIdx, we do nothing; - // otherwise, we should trace the correct ancestor - for minTraceIndex.Cmp(traceIdx) == 1 || maxTraceIndex.Cmp(traceIdx) == -1 { - parent, err := game.GetParent(ancestor) - if err != nil { - return types.DAItem{}, fmt.Errorf("failed to get ancestor of claim %v: %w", ancestor.ContractIndex, err) - } - - ancestor = parent - minTraceIndex = ancestor.TraceIndex(game.MaxDepth()) - offset = new(big.Int).Lsh(big.NewInt(int64(game.MaxAttackBranch())-1), uint(game.MaxDepth()-ancestor.Depth())) - maxTraceIndex = new(big.Int).Add(minTraceIndex, offset) - } - - branch := new(big.Int).Div(new(big.Int).Sub(traceIdx, minTraceIndex), new(big.Int).Lsh(big.NewInt(1), uint(game.MaxDepth()-ancestor.Depth()))).Int64() - subclaims, err := game.GetSubClaims(ancestor) - if err != nil { - return types.DAItem{}, fmt.Errorf("failed to get subclaims of claim %v: %w", ref.ContractIndex, err) - } - - ancestorClaim := subclaims[branch] - merkleProof := make([]byte, 0) - for i := int64(0); i < branch; i++ { - merkleProof = append(merkleProof, subclaims[i].Bytes()...) - } - - for i := branch + 1; i < int64(game.MaxAttackBranch()); i++ { - merkleProof = append(merkleProof, subclaims[i].Bytes()...) - } - - return types.DAItem{ - DaType: types.CallDataType, - DataHash: ancestorClaim.Bytes(), - Proof: merkleProof, - }, nil -} diff --git a/op-challenger/game/fault/trace/split/split2.go b/op-challenger/game/fault/trace/split/split2.go new file mode 100644 index 000000000000..689b276b4ff1 --- /dev/null +++ b/op-challenger/game/fault/trace/split/split2.go @@ -0,0 +1,101 @@ +package split + +import ( + "context" + "fmt" + "math/big" + + "github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace" + "github.com/ethereum-optimism/optimism/op-challenger/game/fault/types" +) + +// Get the traceIdx's accestor claims for multi-sec fault proof. traceIdx can be ref's traceIdx ±1. +func findAncestorWithTraceIndex2(game types.Game, ref types.Claim, depth types.Depth, traceIdx *big.Int) (types.Claim, error) { + // If traceIdx equals -1, the absolute prestate is returned. + if traceIdx.Cmp(big.NewInt(-1)) == 0 { + return types.Claim{}, nil + } + + // If traceIdx is the right most branch, the root Claim is returned. + if new(big.Int).Add(traceIdx, big.NewInt(1)).Cmp(new(big.Int).Lsh(big.NewInt(1), uint(depth))) == 0 { + return game.RootClaim(), nil + } + + ancestor := ref + minTraceIndex := ancestor.TraceIndex(depth) + offset := new(big.Int).Lsh(big.NewInt(int64(game.MaxAttackBranch())-1), uint(depth-ancestor.Depth())) + maxTraceIndex := new(big.Int).Add(minTraceIndex, offset) + // If Ancestor's left most branch traceIdx <= traceId <= Ancestor's rightmost branch traceIdx, we do nothing; + // otherwise, we should trace the correct ancestor + for minTraceIndex.Cmp(traceIdx) == 1 || maxTraceIndex.Cmp(traceIdx) == -1 { + parent, err := game.GetParent(ancestor) + if err != nil { + return types.Claim{}, fmt.Errorf("failed to get ancestor of claim %v: %w", ancestor.ContractIndex, err) + } + + ancestor = parent + minTraceIndex = ancestor.TraceIndex(depth) + offset = new(big.Int).Lsh(big.NewInt(int64(game.MaxAttackBranch())-1), uint(depth-ancestor.Depth())) + maxTraceIndex = new(big.Int).Add(minTraceIndex, offset) + } + + branch := new(big.Int).Div(new(big.Int).Sub(traceIdx, minTraceIndex), new(big.Int).Lsh(big.NewInt(1), uint(depth-ancestor.Depth()))).Int64() + subclaims, err := game.GetSubClaims(ancestor) + if err != nil { + return types.Claim{}, fmt.Errorf("failed to get subclaims of claim %v: %w", ref.ContractIndex, err) + } + + claim := types.Claim{ + ClaimData: types.ClaimData{ + Value: subclaims[branch], + Position: types.NewPositionFromGIndex(traceIdx), + }, + } + + return claim, nil +} + +func NewSplitProviderSelector2(topProvider types.TraceProvider, topDepth types.Depth, bottomProviderCreator ProviderCreator) trace.ProviderSelector { + return func(ctx context.Context, game types.Game, ref types.Claim, pos types.Position) (types.TraceProvider, error) { + if pos.Depth() <= topDepth { + return topProvider, nil + } + if ref.Position.Depth() < topDepth { + return nil, fmt.Errorf("%w, claim depth: %v, depth required: %v", errRefClaimNotDeepEnough, ref.Position.Depth(), topDepth) + } + + // Find the ancestor claim at the (splitDepth + 1) level. + splitLeaf, err := trace.FindAncestorAtDepth(game, ref, types.Depth(topDepth+game.NBits())) + if err != nil { + return nil, err + } + + // Find the ancestor claim at the leaf level splitDepth for the top game. + topLeaf, err := game.GetParent(splitLeaf) + if err != nil { + return nil, err + } + + attackBranch := int64(splitLeaf.AttackBranch) + preTraceIdx := new(big.Int).Add(topLeaf.TraceIndex(topDepth), big.NewInt(attackBranch)) + postTraceIdx := new(big.Int).Add(preTraceIdx, big.NewInt(1)) + pre, err := findAncestorWithTraceIndex2(game, topLeaf, topDepth, preTraceIdx) + if err != nil { + return nil, fmt.Errorf("failed to find pre claim: %w", err) + } + post, err := findAncestorWithTraceIndex2(game, topLeaf, topDepth, postTraceIdx) + if err != nil { + return nil, fmt.Errorf("failed to find post claim: %w", err) + } + + // The top game runs from depth 0 to split depth *inclusive*. + // The - 1 here accounts for the fact that the split depth is included in the top game. + bottomDepth := game.MaxDepth() - topDepth - game.NBits() + provider, err := bottomProviderCreator(ctx, bottomDepth, pre, post) + if err != nil { + return nil, err + } + // Translate such that the root of the bottom game is the level below the top game leaf + return trace.Translate(provider, topDepth+game.NBits()), nil + } +} diff --git a/op-challenger/game/fault/trace/split/split_test.go b/op-challenger/game/fault/trace/split/split_test.go index 2708a4c27104..09b48b05b19e 100644 --- a/op-challenger/game/fault/trace/split/split_test.go +++ b/op-challenger/game/fault/trace/split/split_test.go @@ -166,84 +166,265 @@ func TestBottomProviderAttackingTopLeaf(t *testing.T) { func TestFindAncestorProofAtDepth2(t *testing.T) { var nbits uint64 = 2 nary := int64(1) << nbits - maxGameDepth := types.Depth(4) + maxGameDepth := types.Depth(8) + splitDepth := types.Depth(4) claimBuilder := test.NewAlphabetClaimBuilder(t, big.NewInt(0), maxGameDepth) { // Attack the left most claim - gameBuilder := claimBuilder.GameBuilder2(nary) + expectedTopLeafBranch := uint64(0) + + gameBuilder := claimBuilder.GameBuilder2(nary, splitDepth) + seq := gameBuilder.Seq() + claimBuilder.CreateRootClaim(test.WithValue(common.Hash{0x00})) + subClaimsDep2 := []common.Hash{{0x01}, {0x02}, {0x03}} + seq = seq.Attack2(subClaimsDep2[:], nbits, 0) + + subClaimsDep4 := []common.Hash{{0x04}, {0x05}, {0x06}} // claim at splitDepth + seq = seq.Attack2(subClaimsDep4[:], nbits, expectedTopLeafBranch) + depth4Claim := gameBuilder.Game.Claims()[2] + + subClaimsDep6 := []common.Hash{{0x07}} // claim at splitDepth + 1 + seq = seq.Attack2(subClaimsDep6[:], nbits, 0) + + subClaimsDep8 := []common.Hash{{0x11, 0x12, 0x13}} + seq.Attack2(subClaimsDep8[:], nbits, 0) + depth8Claim := gameBuilder.Game.Claims()[4] + + game := gameBuilder.Game + splitLeaf, err := trace.FindAncestorAtDepth(game, depth8Claim, types.Depth(splitDepth+types.Depth(nbits))) + require.NoError(t, err) + // Find the ancestor claim at the leaf level splitDepth for the top game. + topLeaf, err := game.GetParent(splitLeaf) + require.NoError(t, err) + require.Equal(t, expectedTopLeafBranch, topLeaf.AttackBranch) + + attackBranch := int64(splitLeaf.AttackBranch) + postTraceIdx := new(big.Int).Add(depth4Claim.TraceIndex(splitDepth), big.NewInt(attackBranch)) + preTraceIdx := new(big.Int).Sub(postTraceIdx, big.NewInt(1)) + expectedPre := types.Claim{} + pre, err := findAncestorWithTraceIndex2(game, topLeaf, splitDepth, preTraceIdx) + require.NoError(t, err) + require.Equal(t, expectedPre, pre) + + expectedPost := types.Claim{ + ClaimData: types.ClaimData{ + Value: subClaimsDep4[0], + Position: types.NewPositionFromGIndex(postTraceIdx), + }, + } + post, err := findAncestorWithTraceIndex2(gameBuilder.Game, topLeaf, splitDepth, postTraceIdx) + require.NoError(t, err) + require.Equal(t, expectedPost, post) + } + + { // Attack the left claim + expectedTopLeafBranch := uint64(1) + + gameBuilder := claimBuilder.GameBuilder2(nary, splitDepth) seq := gameBuilder.Seq() claimBuilder.CreateRootClaim(test.WithValue(common.Hash{0x00})) subClaimsDep2 := []common.Hash{{0x01}, {0x02}, {0x03}} seq = seq.Attack2(subClaimsDep2[:], nbits, 0) - subClaimsDep4 := []common.Hash{{0x04}, {0x05}, {0x06}} - seq = seq.Attack2(subClaimsDep4[:], nbits, 0) + subClaimsDep4 := []common.Hash{{0x04}, {0x05}, {0x06}} // claim at splitDepth + seq = seq.Attack2(subClaimsDep4[:], nbits, expectedTopLeafBranch) depth4Claim := gameBuilder.Game.Claims()[2] - subClaimsDep6 := []common.Hash{{0x07}, {0x08}, {0x09}} - seq.Attack2(subClaimsDep6[:], nbits, 0) + subClaimsDep6 := []common.Hash{{0x07}} // claim at splitDepth + 1 + seq = seq.Attack2(subClaimsDep6[:], nbits, 0) + + subClaimsDep8 := []common.Hash{{0x11, 0x12, 0x13}} + seq.Attack2(subClaimsDep8[:], nbits, 0) + depth8Claim := gameBuilder.Game.Claims()[4] - expectedDaItem := types.DAItem{ - DaType: types.CallDataType, - DataHash: gameBuilder.Game.AbsolutePrestate().Value.Bytes(), - Proof: []byte{}, + game := gameBuilder.Game + splitLeaf, err := trace.FindAncestorAtDepth(game, depth8Claim, types.Depth(splitDepth+types.Depth(nbits))) + require.NoError(t, err) + // Find the ancestor claim at the leaf level splitDepth for the top game. + topLeaf, err := game.GetParent(splitLeaf) + require.NoError(t, err) + require.Equal(t, expectedTopLeafBranch, topLeaf.AttackBranch) + + attackBranch := int64(splitLeaf.AttackBranch) + postTraceIdx := new(big.Int).Add(depth4Claim.TraceIndex(splitDepth), big.NewInt(attackBranch)) + preTraceIdx := new(big.Int).Sub(postTraceIdx, big.NewInt(1)) + expectedPre := types.Claim{ + ClaimData: types.ClaimData{ + Value: subClaimsDep2[0], + Position: types.NewPositionFromGIndex(preTraceIdx), + }, } - traceIdx := new(big.Int).Sub(depth4Claim.TraceIndex(gameBuilder.Game.MaxDepth()), big.NewInt(1)) - preStateDaItem, err := findAncestorProofAtDepth2(gameBuilder.Game, depth4Claim, traceIdx) - require.Error(t, err) - require.Equal(t, expectedDaItem, preStateDaItem) + pre, err := findAncestorWithTraceIndex2(game, topLeaf, splitDepth, preTraceIdx) + require.NoError(t, err) + require.Equal(t, expectedPre, pre) + + expectedPost := types.Claim{ + ClaimData: types.ClaimData{ + Value: subClaimsDep4[0], + Position: types.NewPositionFromGIndex(postTraceIdx), + }, + } + post, err := findAncestorWithTraceIndex2(gameBuilder.Game, topLeaf, splitDepth, postTraceIdx) + require.NoError(t, err) + require.Equal(t, expectedPost, post) } { // Attack the middle claim - gameBuilder := claimBuilder.GameBuilder2(nary) + expectedTopLeafBranch := uint64(1) + expectedSplitBranch := uint64(1) + + gameBuilder := claimBuilder.GameBuilder2(nary, splitDepth) + seq := gameBuilder.Seq() + claimBuilder.CreateRootClaim(test.WithValue(common.Hash{0x00})) + subClaimsDep2 := []common.Hash{{0x01}, {0x02}, {0x03}} + seq = seq.Attack2(subClaimsDep2[:], nbits, 0) + + subClaimsDep4 := []common.Hash{{0x04}, {0x05}, {0x06}} // claim at splitDepth + seq = seq.Attack2(subClaimsDep4[:], nbits, expectedTopLeafBranch) + depth4Claim := gameBuilder.Game.Claims()[2] + + subClaimsDep6 := []common.Hash{{0x07}} // claim at splitDepth + 1 + seq = seq.Attack2(subClaimsDep6[:], nbits, expectedSplitBranch) + + subClaimsDep8 := []common.Hash{{0x11, 0x12, 0x13}} + seq.Attack2(subClaimsDep8[:], nbits, 0) + depth8Claim := gameBuilder.Game.Claims()[4] + + game := gameBuilder.Game + splitLeaf, err := trace.FindAncestorAtDepth(game, depth8Claim, types.Depth(splitDepth+types.Depth(nbits))) + require.NoError(t, err) + // Find the ancestor claim at the leaf level splitDepth for the top game. + topLeaf, err := game.GetParent(splitLeaf) + require.NoError(t, err) + require.Equal(t, expectedTopLeafBranch, topLeaf.AttackBranch) + require.Equal(t, expectedSplitBranch, splitLeaf.AttackBranch) + + attackBranch := int64(splitLeaf.AttackBranch) + postTraceIdx := new(big.Int).Add(depth4Claim.TraceIndex(splitDepth), big.NewInt(attackBranch)) + preTraceIdx := new(big.Int).Sub(postTraceIdx, big.NewInt(1)) + expectedPre := types.Claim{ + ClaimData: types.ClaimData{ + Value: subClaimsDep4[0], + Position: types.NewPositionFromGIndex(preTraceIdx), + }, + } + pre, err := findAncestorWithTraceIndex2(game, topLeaf, splitDepth, preTraceIdx) + require.NoError(t, err) + require.Equal(t, expectedPre, pre) + + expectedPost := types.Claim{ + ClaimData: types.ClaimData{ + Value: subClaimsDep4[1], + Position: types.NewPositionFromGIndex(postTraceIdx), + }, + } + post, err := findAncestorWithTraceIndex2(gameBuilder.Game, topLeaf, splitDepth, postTraceIdx) + require.NoError(t, err) + require.Equal(t, expectedPost, post) + } + + { // Attack the right claim + expectedTopLeafBranch := uint64(1) + expectedSplitBranch := uint64(3) + + gameBuilder := claimBuilder.GameBuilder2(nary, splitDepth) seq := gameBuilder.Seq() claimBuilder.CreateRootClaim(test.WithValue(common.Hash{0x00})) subClaimsDep2 := []common.Hash{{0x01}, {0x02}, {0x03}} seq = seq.Attack2(subClaimsDep2[:], nbits, 0) - subClaimsDep4 := []common.Hash{{0x04}, {0x05}, {0x06}} - seq = seq.Attack2(subClaimsDep4[:], nbits, 2) + subClaimsDep4 := []common.Hash{{0x04}, {0x05}, {0x06}} // claim at splitDepth + seq = seq.Attack2(subClaimsDep4[:], nbits, expectedTopLeafBranch) depth4Claim := gameBuilder.Game.Claims()[2] - subClaimsDep6 := []common.Hash{{0x07}, {0x08}, {0x09}} - seq.Attack2(subClaimsDep6[:], nbits, 0) + subClaimsDep6 := []common.Hash{{0x07}} // claim at splitDepth + 1 + seq = seq.Attack2(subClaimsDep6[:], nbits, expectedSplitBranch) + + subClaimsDep8 := []common.Hash{{0x11, 0x12, 0x13}} + seq.Attack2(subClaimsDep8[:], nbits, 0) + depth8Claim := gameBuilder.Game.Claims()[4] + + game := gameBuilder.Game + splitLeaf, err := trace.FindAncestorAtDepth(game, depth8Claim, types.Depth(splitDepth+types.Depth(nbits))) + require.NoError(t, err) + // Find the ancestor claim at the leaf level splitDepth for the top game. + topLeaf, err := game.GetParent(splitLeaf) + require.NoError(t, err) + require.Equal(t, expectedTopLeafBranch, topLeaf.AttackBranch) + require.Equal(t, expectedSplitBranch, splitLeaf.AttackBranch) + + attackBranch := int64(splitLeaf.AttackBranch) + postTraceIdx := new(big.Int).Add(depth4Claim.TraceIndex(splitDepth), big.NewInt(attackBranch)) + preTraceIdx := new(big.Int).Sub(postTraceIdx, big.NewInt(1)) + expectedPre := types.Claim{ + ClaimData: types.ClaimData{ + Value: subClaimsDep4[expectedSplitBranch-1], + Position: types.NewPositionFromGIndex(preTraceIdx), + }, + } + pre, err := findAncestorWithTraceIndex2(game, topLeaf, splitDepth, preTraceIdx) + require.NoError(t, err) + require.Equal(t, expectedPre, pre) - expectedDaItem := types.DAItem{ - DaType: types.CallDataType, - DataHash: subClaimsDep2[1][:], - Proof: append(subClaimsDep2[0][:], subClaimsDep2[2][:]...), + expectedPost := types.Claim{ + ClaimData: types.ClaimData{ + Value: subClaimsDep2[expectedTopLeafBranch], + Position: types.NewPositionFromGIndex(postTraceIdx), + }, } - traceIdx := new(big.Int).Sub(depth4Claim.TraceIndex(gameBuilder.Game.MaxDepth()), big.NewInt(1)) - preStateDaItem, err := findAncestorProofAtDepth2(gameBuilder.Game, depth4Claim, traceIdx) + post, err := findAncestorWithTraceIndex2(gameBuilder.Game, topLeaf, splitDepth, postTraceIdx) require.NoError(t, err) - require.Equal(t, expectedDaItem, preStateDaItem) + require.Equal(t, expectedPost, post) } { // Attack the right most claim - gameBuilder := claimBuilder.GameBuilder2(nary) + expectedTopLeafBranch := uint64(3) + expectedSplitBranch := uint64(3) + + gameBuilder := claimBuilder.GameBuilder2(nary, splitDepth) seq := gameBuilder.Seq() claimBuilder.CreateRootClaim(test.WithValue(common.Hash{0x00})) subClaimsDep2 := []common.Hash{{0x01}, {0x02}, {0x03}} seq = seq.Attack2(subClaimsDep2[:], nbits, 0) - subClaimsDep4 := []common.Hash{{0x04}, {0x05}, {0x06}} - seq = seq.Attack2(subClaimsDep4[:], nbits, 3) + subClaimsDep4 := []common.Hash{{0x04}, {0x05}, {0x06}} // claim at splitDepth + seq = seq.Attack2(subClaimsDep4[:], nbits, expectedTopLeafBranch) depth4Claim := gameBuilder.Game.Claims()[2] - subClaimsDep6 := []common.Hash{{0x07}, {0x08}, {0x09}} - seq.Attack2(subClaimsDep6[:], nbits, 3) + subClaimsDep6 := []common.Hash{{0x07}} // claim at splitDepth + 1 + seq = seq.Attack2(subClaimsDep6[:], nbits, expectedSplitBranch) + + subClaimsDep8 := []common.Hash{{0x11, 0x12, 0x13}} + seq.Attack2(subClaimsDep8[:], nbits, 0) + depth8Claim := gameBuilder.Game.Claims()[4] - expectedDaItem := types.DAItem{ - DaType: types.CallDataType, - DataHash: gameBuilder.Game.RootClaim().Value.Bytes(), - Proof: []byte{}, + game := gameBuilder.Game + splitLeaf, err := trace.FindAncestorAtDepth(game, depth8Claim, types.Depth(splitDepth+types.Depth(nbits))) + require.NoError(t, err) + // Find the ancestor claim at the leaf level splitDepth for the top game. + topLeaf, err := game.GetParent(splitLeaf) + require.NoError(t, err) + require.Equal(t, expectedTopLeafBranch, topLeaf.AttackBranch) + require.Equal(t, expectedSplitBranch, splitLeaf.AttackBranch) + + attackBranch := int64(splitLeaf.AttackBranch) + postTraceIdx := new(big.Int).Add(depth4Claim.TraceIndex(splitDepth), big.NewInt(attackBranch)) + preTraceIdx := new(big.Int).Sub(postTraceIdx, big.NewInt(1)) + expectedPre := types.Claim{ + ClaimData: types.ClaimData{ + Value: subClaimsDep4[expectedSplitBranch-1], + Position: types.NewPositionFromGIndex(preTraceIdx), + }, } - traceIdx := new(big.Int).Add(depth4Claim.TraceIndex(gameBuilder.Game.MaxDepth()), big.NewInt(3)) - preStateDaItem, err := findAncestorProofAtDepth2(gameBuilder.Game, depth4Claim, traceIdx) + pre, err := findAncestorWithTraceIndex2(game, topLeaf, splitDepth, preTraceIdx) require.NoError(t, err) - require.Equal(t, expectedDaItem, preStateDaItem) - } + require.Equal(t, expectedPre, pre) + expectedPost := game.RootClaim() + post, err := findAncestorWithTraceIndex2(gameBuilder.Game, topLeaf, splitDepth, postTraceIdx) + require.NoError(t, err) + require.Equal(t, expectedPost, post) + } } func attackTopLeafGIndex8(_ *testing.T, gameBuilder *test.GameBuilder) (ref types.Claim, pos types.Position, expectPre types.Claim, expectPost types.Claim) { diff --git a/op-challenger/game/fault/types/game.go b/op-challenger/game/fault/types/game.go index 32a1baca7de5..86e7eeccb247 100644 --- a/op-challenger/game/fault/types/game.go +++ b/op-challenger/game/fault/types/game.go @@ -43,7 +43,8 @@ type Game interface { // Returns the claim and true if the ancestor is found, or Claim{}, false if not. AncestorWithTraceIndex(claim Claim, idx *big.Int) (Claim, bool) - AbsolutePrestate() Claim + SplitDepth() Depth + RootDepth() Depth RootClaim() Claim // 1<