Skip to content

Commit

Permalink
feat(op-challenger):add findAncestorProofAtDepth2 func
Browse files Browse the repository at this point in the history
  • Loading branch information
dajuguan committed Oct 22, 2024
1 parent 8db70b8 commit 4784022
Show file tree
Hide file tree
Showing 17 changed files with 516 additions and 4 deletions.
57 changes: 57 additions & 0 deletions op-challenger/game/fault/solver/solver2.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package solver

import (
"context"
"fmt"

"github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
)

// AttemptStep determines what step, if any, should occur for a given leaf claim.
// An error will be returned if the claim is not at the max depth.
// Returns nil, nil if no step should be performed.
func (s *claimSolver) AttemptStep2(ctx context.Context, game types.Game, claim types.Claim, honestClaims *honestClaimTracker) (*StepData, error) {
if claim.Depth() != s.gameDepth {
return nil, ErrStepNonLeafNode
}

if counter, err := s.shouldCounter(game, claim, honestClaims); err != nil {
return nil, fmt.Errorf("failed to determine if claim should be countered: %w", err)
} else if !counter {
return nil, nil
}

claimCorrect, err := s.agreeWithClaim(ctx, game, claim)
if err != nil {
return nil, err
}

// prestateItem
// postStateItem

var position types.Position
if !claimCorrect {
// Attack the claim by executing step index, so we need to get the pre-state of that index
position = claim.Position
} else {
// Defend and use this claim as the starting point to execute the step after.
// Thus, we need the pre-state of the next step.
position = claim.Position.MoveRight()
}

preState, proofData, oracleData, err := s.trace.GetStepData(ctx, game, claim, position)
if err != nil {
return nil, err
}

return &StepData{
LeafClaim: claim,
IsAttack: !claimCorrect,
PreState: preState,
ProofData: proofData,
OracleData: oracleData,
}, nil
}


func findPrestateItem()
25 changes: 25 additions & 0 deletions op-challenger/game/fault/test/claim_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (

"github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/stretchr/testify/require"
)

Expand Down Expand Up @@ -174,3 +175,27 @@ func (c *ClaimBuilder) DefendClaim(claim types.Claim, opts ...ClaimOpt) types.Cl
pos := claim.Position.Defend()
return c.claim(pos, append([]ClaimOpt{WithParent(claim)}, opts...)...)
}

func GetClaimsHash(values []common.Hash) common.Hash {
nelem := len(values)
hashes := make([]common.Hash, nelem)
copy(hashes, values)
for nelem != 1 {
for i := 0; i < nelem/2; i++ {
hashes[i] = crypto.Keccak256Hash(hashes[i][:], hashes[i+1][:])
}
// directly copy the last item
if nelem%2 == 1 {
hashes[nelem/2] = hashes[nelem-1]
}
nelem = (nelem + 1) / 2
}

return hashes[0]
}

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...)...)
}
17 changes: 17 additions & 0 deletions op-challenger/game/fault/test/claim_builder_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package test

import (
"testing"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/stretchr/testify/require"
)

func TestGetClaimsHash(t *testing.T) {
claims := []common.Hash{{0x01}, {0x02}, {0x03}}
rootHash := GetClaimsHash(claims)
expectedHash := crypto.Keccak256Hash(claims[0][:], claims[1][:])
expectedHash = crypto.Keccak256Hash(expectedHash[:], claims[2][:])
require.Equal(t, expectedHash, rootHash)
}
28 changes: 28 additions & 0 deletions op-challenger/game/fault/test/game_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,13 @@ func (c *ClaimBuilder) GameBuilder(rootOpts ...ClaimOpt) *GameBuilder {
}
}

func (c *ClaimBuilder) GameBuilder2(nary int64, rootOpts ...ClaimOpt) *GameBuilder {
return &GameBuilder{
builder: c,
Game: types.NewGameState2([]types.Claim{c.CreateRootClaim(rootOpts...)}, c.maxDepth, nary, make(map[types.ClaimID][]common.Hash)),
}
}

type GameBuilderSeq struct {
gameBuilder *GameBuilder
builder *ClaimBuilder
Expand Down Expand Up @@ -67,6 +74,17 @@ func (s *GameBuilderSeq) addClaimToGame(claim *types.Claim) {
s.gameBuilder.Game = types.NewGameState(claims, s.builder.maxDepth)
}

func (s *GameBuilderSeq) addClaimAndSubClaimsToGame(claim *types.Claim, subClaims []common.Hash) {
if s.gameBuilder.Game.IsDuplicate(*claim) {
return
}
claim.ContractIndex = len(s.gameBuilder.Game.Claims())
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)
}

func (s *GameBuilderSeq) Attack(opts ...ClaimOpt) *GameBuilderSeq {
claim := s.builder.AttackClaim(s.lastClaim, opts...)
s.addClaimToGame(&claim)
Expand All @@ -77,6 +95,16 @@ func (s *GameBuilderSeq) Attack(opts ...ClaimOpt) *GameBuilderSeq {
}
}

func (s *GameBuilderSeq) Attack2(subClaims []common.Hash, nbits uint64, branch uint64, opts ...ClaimOpt) *GameBuilderSeq {
claim := s.builder.AttackClaim2(s.lastClaim, subClaims, nbits, branch, opts...)
s.addClaimAndSubClaimsToGame(&claim, subClaims)
return &GameBuilderSeq{
gameBuilder: s.gameBuilder,
builder: s.builder,
lastClaim: claim,
}
}

func (s *GameBuilderSeq) Defend(opts ...ClaimOpt) *GameBuilderSeq {
claim := s.builder.DefendClaim(s.lastClaim, opts...)
s.addClaimToGame(&claim)
Expand Down
19 changes: 19 additions & 0 deletions op-challenger/game/fault/trace/access2.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package trace

import (
"context"

"github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
)

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) {
provider, err := t.selector(ctx, game, ref, pos)
if err != nil {
return nil, nil, nil, err
}
parentPos, err := game.GetParent(ref)
if err != nil {
return nil, nil, nil, err
}
return provider.GetStepData2(ctx, pos, parentPos.Position)
}
1 change: 1 addition & 0 deletions op-challenger/game/fault/trace/alphabet/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ type AlphabetTraceProvider struct {
startingBlockNumber *big.Int
depth types.Depth
maxLen uint64
splitDepth types.Depth
}

// NewTraceProvider returns a new [AlphabetProvider].
Expand Down
29 changes: 29 additions & 0 deletions op-challenger/game/fault/trace/alphabet/provider2.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package alphabet

import (
"context"
"fmt"
"math/big"

"github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
preimage "github.com/ethereum-optimism/optimism/op-preimage"
"github.com/ethereum/go-ethereum/common"
)

func (ap *AlphabetTraceProvider) GetStepData2(ctx context.Context, pos types.Position, parentPos types.Position) ([]byte, []byte, *types.PreimageOracleData, error) {
traceIndex := pos.TraceIndex(ap.depth)
key := preimage.LocalIndexKey(L2ClaimBlockNumberLocalIndex).PreimageKey()
preimageData := types.NewPreimageOracleData(key[:], ap.startingBlockNumber.Bytes(), 0)
if traceIndex.Cmp(common.Big0) == 0 {
return absolutePrestate, []byte{}, preimageData, nil
}
if traceIndex.Cmp(new(big.Int).SetUint64(ap.maxLen)) > 0 {
return nil, nil, nil, fmt.Errorf("%w depth: %v index: %v max: %v", ErrIndexTooLarge, ap.depth, traceIndex, ap.maxLen)
}
initialTraceIndex := new(big.Int).Lsh(ap.startingBlockNumber, uint(ap.splitDepth))
initialClaim := new(big.Int).Add(absolutePrestateInt, initialTraceIndex)
newTraceIndex := new(big.Int).Add(initialTraceIndex, traceIndex)
// newClaim is always, otherwise we would counter the first false subClaim
newClaim := new(big.Int).Add(initialClaim, traceIndex)
return BuildAlphabetPreimage(newTraceIndex, newClaim), []byte{}, preimageData, nil
}
33 changes: 33 additions & 0 deletions op-challenger/game/fault/trace/asterisc/provider2.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package asterisc

import (
"context"
"errors"
"fmt"

"github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
)

func (p *AsteriscTraceProvider) GetStepData2(ctx context.Context, pos types.Position, parentPos types.Position) ([]byte, []byte, *types.PreimageOracleData, error) {
traceIndex := pos.TraceIndex(p.gameDepth)
if !traceIndex.IsUint64() {
return nil, nil, nil, errors.New("trace index out of bounds")
}
proof, err := p.loadProof(ctx, traceIndex.Uint64())
if err != nil {
return nil, nil, nil, err
}
value := ([]byte)(proof.StateData)
if len(value) == 0 {
return nil, nil, nil, errors.New("proof missing state data")
}
data := ([]byte)(proof.ProofData)
if data == nil {
return nil, nil, nil, errors.New("proof missing proof data")
}
oracleData, err := p.preimageLoader.LoadPreimage(proof)
if err != nil {
return nil, nil, nil, fmt.Errorf("failed to load preimage: %w", err)
}
return value, data, oracleData, nil
}
33 changes: 33 additions & 0 deletions op-challenger/game/fault/trace/cannon/provider2.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package cannon

import (
"context"
"errors"
"fmt"

"github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
)

func (p *CannonTraceProvider) GetStepData2(ctx context.Context, pos types.Position, parentPos types.Position) ([]byte, []byte, *types.PreimageOracleData, error) {
traceIndex := pos.TraceIndex(p.gameDepth)
if !traceIndex.IsUint64() {
return nil, nil, nil, errors.New("trace index out of bounds")
}
proof, err := p.loadProof(ctx, traceIndex.Uint64())
if err != nil {
return nil, nil, nil, err
}
value := ([]byte)(proof.StateData)
if len(value) == 0 {
return nil, nil, nil, errors.New("proof missing state data")
}
data := ([]byte)(proof.ProofData)
if data == nil {
return nil, nil, nil, errors.New("proof missing proof data")
}
oracleData, err := p.preimageLoader.LoadPreimage(proof)
if err != nil {
return nil, nil, nil, fmt.Errorf("failed to load preimage: %w", err)
}
return value, data, oracleData, nil
}
5 changes: 5 additions & 0 deletions op-challenger/game/fault/trace/outputs/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,11 @@ func (o *OutputTraceProvider) GetStepData(_ context.Context, _ types.Position) (
return nil, nil, nil, ErrGetStepData
}

// GetStepData2 is not supported in the [OutputTraceProvider].
func (o *OutputTraceProvider) GetStepData2(_ context.Context, _ types.Position, _ types.Position) (prestate []byte, proofData []byte, preimageData *types.PreimageOracleData, err error) {
return nil, nil, nil, ErrGetStepData
}

func (o *OutputTraceProvider) GetL2BlockNumberChallenge(ctx context.Context) (*types.InvalidL2BlockNumberChallenge, error) {
outputBlock, err := o.HonestBlockNumber(ctx, types.RootPosition)
if err != nil {
Expand Down
61 changes: 61 additions & 0 deletions op-challenger/game/fault/trace/split/split.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,3 +90,64 @@ 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
}
Loading

0 comments on commit 4784022

Please sign in to comment.