Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
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
8 changes: 5 additions & 3 deletions op-challenger2/game/fault/agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ func (a *Agent) performAction(ctx context.Context, wg *sync.WaitGroup, action ty
containsOracleData := action.OracleData != nil
isLocal := containsOracleData && action.OracleData.IsLocal
actionLog = actionLog.New(
"is_attack", action.IsAttack,
"attackBranch", action.AttackBranch,
"parent", action.ParentClaim.ContractIndex,
"prestate", common.Bytes2Hex(action.PreState),
"proof", common.Bytes2Hex(action.ProofData),
Expand All @@ -133,13 +133,15 @@ func (a *Agent) performAction(ctx context.Context, wg *sync.WaitGroup, action ty
if action.OracleData != nil {
actionLog = actionLog.New("oracleKey", common.Bytes2Hex(action.OracleData.OracleKey))
}
} else if action.Type == types.ActionTypeMove {
actionLog = actionLog.New("is_attack", action.IsAttack, "parent", action.ParentClaim.ContractIndex, "value", action.Value)
} else if action.Type == types.ActionTypeAttackV2 {
actionLog = actionLog.New("attackBranch", action.AttackBranch, "parent", action.ParentClaim.ContractIndex, "value", action.Value)
}

switch action.Type {
case types.ActionTypeMove:
a.metrics.RecordGameMove()
case types.ActionTypeAttackV2:
a.metrics.RecordGameAttackV2()
case types.ActionTypeStep:
a.metrics.RecordGameStep()
case types.ActionTypeChallengeL2BlockNumber:
Expand Down
17 changes: 16 additions & 1 deletion op-challenger2/game/fault/responder/responder.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"errors"
"fmt"
"math/big"

"github.com/ethereum-optimism/optimism/op-challenger2/game/fault/preimages"
"github.com/ethereum-optimism/optimism/op-challenger2/game/fault/types"
Expand All @@ -19,8 +20,10 @@ type GameContract interface {
CallResolveClaim(ctx context.Context, claimIdx uint64) error
ResolveClaimTx(claimIdx uint64) (txmgr.TxCandidate, error)
AttackTx(ctx context.Context, parent types.Claim, pivot common.Hash) (txmgr.TxCandidate, error)
AttackV2Tx(ctx context.Context, parent types.Claim, attackBranch uint64, daType uint64, claims []byte) (txmgr.TxCandidate, error)
DefendTx(ctx context.Context, parent types.Claim, pivot common.Hash) (txmgr.TxCandidate, error)
StepTx(claimIdx uint64, isAttack bool, stateData []byte, proof []byte) (txmgr.TxCandidate, error)
StepV2Tx(claimIdx uint64, attackBranch uint64, stateData []byte, proof types.StepProof) (txmgr.TxCandidate, error)
ChallengeL2BlockNumberTx(challenge *types.InvalidL2BlockNumberChallenge) (txmgr.TxCandidate, error)
}

Expand Down Expand Up @@ -117,8 +120,20 @@ func (r *FaultResponder) PerformAction(ctx context.Context, action types.Action)
} else {
candidate, err = r.contract.DefendTx(ctx, action.ParentClaim, action.Value)
}
case types.ActionTypeAttackV2:
subValues := make([]byte, 0, len(*action.SubValues))
for _, subValue := range *action.SubValues {
subValues = append(subValues, subValue[:]...)
}
daTypeUint64 := (*big.Int)(action.DAType).Uint64()
candidate, err = r.contract.AttackV2Tx(ctx, action.ParentClaim, action.AttackBranch, daTypeUint64, subValues)
case types.ActionTypeStep:
candidate, err = r.contract.StepTx(uint64(action.ParentClaim.ContractIndex), action.IsAttack, action.PreState, action.ProofData)
stepProof := types.StepProof{
PreStateItem: action.OracleData.VMStateDA.PreDA,
PostStateItem: action.OracleData.VMStateDA.PostDA,
VmProof: action.ProofData,
}
candidate, err = r.contract.StepV2Tx(uint64(action.ParentClaim.ContractIndex), action.AttackBranch, action.PreState, stepProof)
case types.ActionTypeChallengeL2BlockNumber:
candidate, err = r.contract.ChallengeL2BlockNumberTx(action.InvalidL2BlockNumberChallenge)
}
Expand Down
69 changes: 51 additions & 18 deletions op-challenger2/game/fault/responder/responder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -146,37 +146,54 @@ func TestPerformAction(t *testing.T) {
require.Equal(t, ([]byte)("attack"), mockTxMgr.sent[0].TxData)
})

t.Run("defend", func(t *testing.T) {
t.Run("attackV2", func(t *testing.T) {
responder, mockTxMgr, contract, _, _ := newTestFaultResponder(t)
action := types.Action{
Type: types.ActionTypeMove,
ParentClaim: types.Claim{ContractIndex: 123},
IsAttack: false,
Value: common.Hash{0xaa},
Type: types.ActionTypeAttackV2,
ParentClaim: types.Claim{ContractIndex: 123},
IsAttack: false,
AttackBranch: 0,
DAType: types.CallDataType,
SubValues: &[]common.Hash{{0xaa}},
}
err := responder.PerformAction(context.Background(), action)
require.NoError(t, err)

require.Len(t, mockTxMgr.sent, 1)
require.EqualValues(t, []interface{}{action.ParentClaim, action.Value}, contract.defendArgs)
require.Equal(t, ([]byte)("defend"), mockTxMgr.sent[0].TxData)
daTypeUint64 := (*big.Int)(action.DAType).Uint64()
subValues := make([]byte, 0, len(*action.SubValues))
for _, subValue := range *action.SubValues {
subValues = append(subValues, subValue[:]...)
}
require.EqualValues(t, []interface{}{action.ParentClaim, action.AttackBranch, daTypeUint64, subValues}, contract.attackV2Args)
require.Equal(t, ([]byte)("attackV2"), mockTxMgr.sent[0].TxData)
})

t.Run("step", func(t *testing.T) {
responder, mockTxMgr, contract, _, _ := newTestFaultResponder(t)
action := types.Action{
Type: types.ActionTypeStep,
ParentClaim: types.Claim{ContractIndex: 123},
IsAttack: true,
PreState: []byte{1, 2, 3},
ProofData: []byte{4, 5, 6},
Type: types.ActionTypeStep,
ParentClaim: types.Claim{ContractIndex: 123},
IsAttack: true,
AttackBranch: 0,
PreState: []byte{1, 2, 3},
ProofData: []byte{4, 5, 6},
OracleData: &types.PreimageOracleData{
VMStateDA: types.DAData{},
OutputRootDAItem: types.DAItem{},
},
}
stepProof := types.StepProof{
PreStateItem: action.OracleData.VMStateDA.PreDA,
PostStateItem: action.OracleData.VMStateDA.PostDA,
VmProof: action.ProofData,
}
err := responder.PerformAction(context.Background(), action)
require.NoError(t, err)

require.Len(t, mockTxMgr.sent, 1)
require.EqualValues(t, []interface{}{uint64(123), action.IsAttack, action.PreState, action.ProofData}, contract.stepArgs)
require.Equal(t, ([]byte)("step"), mockTxMgr.sent[0].TxData)
require.EqualValues(t, []interface{}{uint64(123), action.AttackBranch, action.PreState, stepProof}, contract.stepV2Args)
require.Equal(t, ([]byte)("stepV2"), mockTxMgr.sent[0].TxData)
})

t.Run("stepWithLocalOracleData", func(t *testing.T) {
Expand All @@ -188,15 +205,17 @@ func TestPerformAction(t *testing.T) {
PreState: []byte{1, 2, 3},
ProofData: []byte{4, 5, 6},
OracleData: &types.PreimageOracleData{
IsLocal: true,
IsLocal: true,
VMStateDA: types.DAData{},
OutputRootDAItem: types.DAItem{},
},
}
err := responder.PerformAction(context.Background(), action)
require.NoError(t, err)

require.Len(t, mockTxMgr.sent, 1)
require.Nil(t, contract.updateOracleArgs) // mock uploader returns nil
require.Equal(t, ([]byte)("step"), mockTxMgr.sent[0].TxData)
require.Equal(t, ([]byte)("stepV2"), mockTxMgr.sent[0].TxData)
require.Equal(t, 1, uploader.updates)
require.Equal(t, 0, oracle.existCalls)
})
Expand All @@ -210,15 +229,17 @@ func TestPerformAction(t *testing.T) {
PreState: []byte{1, 2, 3},
ProofData: []byte{4, 5, 6},
OracleData: &types.PreimageOracleData{
IsLocal: false,
IsLocal: false,
VMStateDA: types.DAData{},
OutputRootDAItem: types.DAItem{},
},
}
err := responder.PerformAction(context.Background(), action)
require.NoError(t, err)

require.Len(t, mockTxMgr.sent, 1)
require.Nil(t, contract.updateOracleArgs) // mock uploader returns nil
require.Equal(t, ([]byte)("step"), mockTxMgr.sent[0].TxData)
require.Equal(t, ([]byte)("stepV2"), mockTxMgr.sent[0].TxData)
require.Equal(t, 1, uploader.updates)
require.Equal(t, 1, oracle.existCalls)
})
Expand Down Expand Up @@ -370,8 +391,10 @@ type mockContract struct {
calls int
callFails bool
attackArgs []interface{}
attackV2Args []interface{}
defendArgs []interface{}
stepArgs []interface{}
stepV2Args []interface{}
challengeArgs []interface{}
updateOracleClaimIdx uint64
updateOracleArgs *types.PreimageOracleData
Expand Down Expand Up @@ -411,6 +434,11 @@ func (m *mockContract) AttackTx(_ context.Context, parent types.Claim, claim com
return txmgr.TxCandidate{TxData: ([]byte)("attack")}, nil
}

func (m *mockContract) AttackV2Tx(ctx context.Context, parent types.Claim, attackBranch uint64, daType uint64, claims []byte) (txmgr.TxCandidate, error) {
m.attackV2Args = []interface{}{parent, attackBranch, daType, claims}
return txmgr.TxCandidate{TxData: ([]byte)("attackV2")}, nil
}

func (m *mockContract) DefendTx(_ context.Context, parent types.Claim, claim common.Hash) (txmgr.TxCandidate, error) {
m.defendArgs = []interface{}{parent, claim}
return txmgr.TxCandidate{TxData: ([]byte)("defend")}, nil
Expand All @@ -421,6 +449,11 @@ func (m *mockContract) StepTx(claimIdx uint64, isAttack bool, stateData []byte,
return txmgr.TxCandidate{TxData: ([]byte)("step")}, nil
}

func (m *mockContract) StepV2Tx(claimIdx uint64, attackBranch uint64, stateData []byte, proof types.StepProof) (txmgr.TxCandidate, error) {
m.stepV2Args = []interface{}{claimIdx, attackBranch, stateData, proof}
return txmgr.TxCandidate{TxData: ([]byte)("stepV2")}, nil
}

func (m *mockContract) UpdateOracleTx(_ context.Context, claimIdx uint64, data *types.PreimageOracleData) (txmgr.TxCandidate, error) {
m.updateOracleClaimIdx = claimIdx
m.updateOracleArgs = data
Expand Down
54 changes: 42 additions & 12 deletions op-challenger2/game/fault/solver/actors.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,20 +51,28 @@ var correctDefendLastClaim = respondLastClaim(func(seq *test.GameBuilderSeq) {
// Must attack the root
seq.Attack2(nil, 0)
} else {
seq.Attack2(nil, 1)
seq.Attack2(nil, seq.MaxAttackBranch())
}
})

var incorrectAttackLastClaim = respondLastClaim(func(seq *test.GameBuilderSeq) {
seq.Attack2(nil, 0, test.WithValue(common.Hash{0xaa}))
incorrectSubValues := []common.Hash{}
for i := uint64(0); i < seq.MaxAttackBranch(); i++ {
incorrectSubValues = append(incorrectSubValues, common.Hash{0xaa})
}
seq.Attack2(incorrectSubValues, 0)
})

var incorrectDefendLastClaim = respondLastClaim(func(seq *test.GameBuilderSeq) {
incorrectSubValues := []common.Hash{}
for i := uint64(0); i < seq.MaxAttackBranch(); i++ {
incorrectSubValues = append(incorrectSubValues, common.Hash{0xdd})
}
if seq.IsRoot() {
// Must attack the root
seq.Attack2(nil, 0, test.WithValue(common.Hash{0xdd}))
seq.Attack2(incorrectSubValues, 0)
} else {
seq.Attack2(nil, 1, test.WithValue(common.Hash{0xdd}))
seq.Attack2(incorrectSubValues, seq.MaxAttackBranch())
}
})

Expand All @@ -77,29 +85,51 @@ var defendEverythingCorrect = respondAllClaims(func(seq *test.GameBuilderSeq) {
// Must attack root
seq.Attack2(nil, 0)
} else {
seq.Attack2(nil, 1)
seq.Attack2(nil, seq.MaxAttackBranch())
}
})

var attackEverythingIncorrect = respondAllClaims(func(seq *test.GameBuilderSeq) {
seq.Attack2(nil, 0, test.WithValue(common.Hash{0xaa}))
incorrectSubValues := []common.Hash{}
for i := uint64(0); i < seq.MaxAttackBranch(); i++ {
incorrectSubValues = append(incorrectSubValues, common.Hash{0xaa})
}
seq.Attack2(incorrectSubValues, 0)
})

var defendEverythingIncorrect = respondAllClaims(func(seq *test.GameBuilderSeq) {
incorrectSubValues := []common.Hash{}
for i := uint64(0); i < seq.MaxAttackBranch(); i++ {
incorrectSubValues = append(incorrectSubValues, common.Hash{0xbb})
}
if seq.IsRoot() {
// Must attack root
seq.Attack2(nil, 0, test.WithValue(common.Hash{0xbb}))
seq.Attack2(incorrectSubValues, 0)
} else {
seq.Attack2(nil, 1, test.WithValue(common.Hash{0xbb}))
seq.Attack2(incorrectSubValues, seq.MaxAttackBranch())
}
})

var exhaustive = respondAllClaims(func(seq *test.GameBuilderSeq) {
seq.Attack2(nil, 0)
seq.Attack2(nil, 0, test.WithValue(common.Hash{0xaa}))
if !seq.IsRoot() {
seq.Attack2(nil, 1)
seq.Attack2(nil, 1, test.WithValue(common.Hash{0xdd}))
incorrectSubValues := []common.Hash{}
for i := uint64(0); i < seq.MaxAttackBranch(); i++ {
incorrectSubValues = append(incorrectSubValues, common.Hash{0xaa})
if seq.IsSplitDepth() {
// at splitDepth, there is only one subValue
break
}
}
seq.Attack2(incorrectSubValues, 0)
if !seq.IsRoot() && !seq.IsTraceRoot() {
seq.Attack2(nil, seq.MaxAttackBranch())
for i := uint64(0); i < seq.MaxAttackBranch(); i++ {
incorrectSubValues[i] = common.Hash{0xdd}
if seq.IsSplitDepth() {
break
}
}
seq.Attack2(incorrectSubValues, seq.MaxAttackBranch())
}
})

Expand Down
1 change: 0 additions & 1 deletion op-challenger2/game/fault/solver/game_rules_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import (

func verifyGameRules(t *testing.T, game types.Game, rootClaimCorrect bool) {
actualResult, claimTree, resolvedGame := gameResult(game)

verifyExpectedGameResult(t, rootClaimCorrect, actualResult)

verifyNoChallengerClaimsWereSuccessfullyCountered(t, resolvedGame)
Expand Down
Loading