Commit eb43f71a authored by Adrian Sutton's avatar Adrian Sutton Committed by GitHub

op-dispute-mon: Fix resolution to respect step actions (#9635)

* op-dispute-mon: Fix resolution to respect step actions
op-challenger: Add skipped game_solver tests for freeloader claims

* op-challenger: Add addition freeloader test.
parent 889b59c7
...@@ -3,18 +3,32 @@ package solver ...@@ -3,18 +3,32 @@ package solver
import ( import (
"context" "context"
"encoding/hex" "encoding/hex"
"fmt"
"math/big" "math/big"
"testing" "testing"
faulttest "github.com/ethereum-optimism/optimism/op-challenger/game/fault/test" faulttest "github.com/ethereum-optimism/optimism/op-challenger/game/fault/test"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace" "github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/types" "github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
gameTypes "github.com/ethereum-optimism/optimism/op-challenger/game/types"
"github.com/ethereum-optimism/optimism/op-dispute-mon/mon/resolution"
"github.com/ethereum-optimism/optimism/op-dispute-mon/mon/transform"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
const expectFreeloaderCounters = false
type RunCondition uint8
const (
RunAlways RunCondition = iota
RunFreeloadersCountered
RunFreeloadersNotCountered
)
func TestCalculateNextActions(t *testing.T) { func TestCalculateNextActions(t *testing.T) {
maxDepth := types.Depth(4) maxDepth := types.Depth(6)
startingL2BlockNumber := big.NewInt(0) startingL2BlockNumber := big.NewInt(0)
claimBuilder := faulttest.NewAlphabetClaimBuilder(t, startingL2BlockNumber, maxDepth) claimBuilder := faulttest.NewAlphabetClaimBuilder(t, startingL2BlockNumber, maxDepth)
...@@ -22,6 +36,7 @@ func TestCalculateNextActions(t *testing.T) { ...@@ -22,6 +36,7 @@ func TestCalculateNextActions(t *testing.T) {
name string name string
rootClaimCorrect bool rootClaimCorrect bool
setupGame func(builder *faulttest.GameBuilder) setupGame func(builder *faulttest.GameBuilder)
runCondition RunCondition
}{ }{
{ {
name: "AttackRootClaim", name: "AttackRootClaim",
...@@ -61,6 +76,8 @@ func TestCalculateNextActions(t *testing.T) { ...@@ -61,6 +76,8 @@ func TestCalculateNextActions(t *testing.T) {
lastHonestClaim := builder.Seq(). lastHonestClaim := builder.Seq().
AttackCorrect(). AttackCorrect().
AttackCorrect(). AttackCorrect().
DefendCorrect().
DefendCorrect().
DefendCorrect() DefendCorrect()
lastHonestClaim.AttackCorrect().ExpectStepDefend() lastHonestClaim.AttackCorrect().ExpectStepDefend()
lastHonestClaim.Attack(common.Hash{0xdd}).ExpectStepAttack() lastHonestClaim.Attack(common.Hash{0xdd}).ExpectStepAttack()
...@@ -84,18 +101,119 @@ func TestCalculateNextActions(t *testing.T) { ...@@ -84,18 +101,119 @@ func TestCalculateNextActions(t *testing.T) {
Attack(maliciousStateHash) Attack(maliciousStateHash)
}, },
}, },
{
name: "Freeloader-ValidClaimAtInvalidAttackPosition",
setupGame: func(builder *faulttest.GameBuilder) {
builder.Seq().
AttackCorrect(). // Honest response to invalid root
DefendCorrect().ExpectDefend(). // Defender agrees at this point, we should defend
AttackCorrect().ExpectDefend() // Freeloader attacks instead of defends
},
runCondition: RunFreeloadersCountered,
},
{
name: "Freeloader-InvalidClaimAtInvalidAttackPosition",
setupGame: func(builder *faulttest.GameBuilder) {
builder.Seq().
AttackCorrect(). // Honest response to invalid root
DefendCorrect().ExpectDefend(). // Defender agrees at this point, we should defend
Attack(common.Hash{0xbb}).ExpectAttack() // Freeloader attacks with wrong claim instead of defends
},
runCondition: RunFreeloadersCountered,
},
{
name: "Freeloader-InvalidClaimAtValidDefensePosition",
setupGame: func(builder *faulttest.GameBuilder) {
builder.Seq().
AttackCorrect(). // Honest response to invalid root
DefendCorrect().ExpectDefend(). // Defender agrees at this point, we should defend
Defend(common.Hash{0xbb}).ExpectAttack() // Freeloader defends with wrong claim, we should attack
},
runCondition: RunFreeloadersCountered,
},
{
name: "Freeloader-InvalidClaimAtValidAttackPosition",
setupGame: func(builder *faulttest.GameBuilder) {
builder.Seq().
AttackCorrect(). // Honest response to invalid root
Defend(common.Hash{0xaa}).ExpectAttack(). // Defender disagrees at this point, we should attack
Attack(common.Hash{0xbb}).ExpectAttack() // Freeloader attacks with wrong claim instead of defends
},
runCondition: RunFreeloadersCountered,
},
{
name: "Freeloader-InvalidClaimAtInvalidDefensePosition",
setupGame: func(builder *faulttest.GameBuilder) {
builder.Seq().
AttackCorrect(). // Honest response to invalid root
Defend(common.Hash{0xaa}).ExpectAttack(). // Defender disagrees at this point, we should attack
Defend(common.Hash{0xbb}) // Freeloader defends with wrong claim but we must not respond to avoid poisoning
},
},
{
name: "Freeloader-ValidClaimAtInvalidAttackPosition-RespondingToDishonestButCorrectAttack",
setupGame: func(builder *faulttest.GameBuilder) {
builder.Seq().
AttackCorrect(). // Honest response to invalid root
AttackCorrect().ExpectDefend(). // Defender attacks with correct value, we should defend
AttackCorrect().ExpectDefend() // Freeloader attacks with wrong claim, we should defend
},
runCondition: RunFreeloadersCountered,
},
{
name: "Freeloader-DoNotCounterOwnClaim",
setupGame: func(builder *faulttest.GameBuilder) {
builder.Seq().
AttackCorrect(). // Honest response to invalid root
AttackCorrect().ExpectDefend(). // Defender attacks with correct value, we should defend
AttackCorrect(). // Freeloader attacks instead, we should defend
DefendCorrect() // We do defend and we shouldn't counter our own claim
},
runCondition: RunFreeloadersCountered,
},
{
name: "Freeloader-ContinueDefendingAgainstFreeloader",
setupGame: func(builder *faulttest.GameBuilder) {
builder.Seq(). // invalid root
AttackCorrect(). // Honest response to invalid root
AttackCorrect().ExpectDefend(). // Defender attacks with correct value, we should defend
AttackCorrect(). // Freeloader attacks instead, we should defend
DefendCorrect(). // We do defend
Attack(common.Hash{0xaa}). // freeloader attacks our defense, we should attack
ExpectAttack()
},
runCondition: RunFreeloadersCountered,
},
{
name: "Freeloader-FreeloaderCountersRootClaim",
setupGame: func(builder *faulttest.GameBuilder) {
builder.Seq().
ExpectAttack(). // Honest response to invalid root
Attack(common.Hash{0xaa}). // freeloader
ExpectAttack() // Honest response to freeloader
},
runCondition: RunFreeloadersCountered,
},
} }
for _, test := range tests { for _, test := range tests {
test := test test := test
t.Run(test.name, func(t *testing.T) { t.Run(test.name, func(t *testing.T) {
switch test.runCondition {
case RunAlways:
case RunFreeloadersCountered:
if !expectFreeloaderCounters {
t.Skip("Freeloader countering not enabled")
}
case RunFreeloadersNotCountered:
if expectFreeloaderCounters {
t.Skip("Freeloader countering enabled")
}
}
builder := claimBuilder.GameBuilder(test.rootClaimCorrect) builder := claimBuilder.GameBuilder(test.rootClaimCorrect)
test.setupGame(builder) test.setupGame(builder)
game := builder.Game game := builder.Game
for i, claim := range game.Claims() { logClaims(t, game)
t.Logf("Claim %v: Pos: %v TraceIdx: %v ParentIdx: %v, CounteredBy: %v, Value: %v",
i, claim.Position.ToGIndex(), claim.Position.TraceIndex(maxDepth), claim.ParentContractIndex, claim.CounteredBy, claim.Value)
}
solver := NewGameSolver(maxDepth, trace.NewSimpleTraceAccessor(claimBuilder.CorrectTraceProvider())) solver := NewGameSolver(maxDepth, trace.NewSimpleTraceAccessor(claimBuilder.CorrectTraceProvider()))
actions, err := solver.CalculateNextActions(context.Background(), game) actions, err := solver.CalculateNextActions(context.Background(), game)
...@@ -112,6 +230,61 @@ func TestCalculateNextActions(t *testing.T) { ...@@ -112,6 +230,61 @@ func TestCalculateNextActions(t *testing.T) {
require.Containsf(t, actions, action, "Expected claim %v missing", i) require.Containsf(t, actions, action, "Expected claim %v missing", i)
} }
require.Len(t, actions, len(builder.ExpectedActions), "Incorrect number of actions") require.Len(t, actions, len(builder.ExpectedActions), "Incorrect number of actions")
challengerAddr := common.Address{0xaa, 0xbb, 0xcc, 0xdd}
postState := applyActions(game, challengerAddr, actions)
t.Log("Post game state:")
logClaims(t, postState)
actualResult := gameResult(postState)
expectedResult := gameTypes.GameStatusChallengerWon
if test.rootClaimCorrect {
expectedResult = gameTypes.GameStatusDefenderWon
}
require.Equalf(t, expectedResult, actualResult, "Game should resolve correctly expected %v but was %v", expectedResult, actualResult)
}) })
} }
} }
func logClaims(t *testing.T, game types.Game) {
for i, claim := range game.Claims() {
t.Logf("Claim %v: Pos: %v TraceIdx: %v Depth: %v IndexAtDepth: %v ParentIdx: %v Value: %v Claimant: %v CounteredBy: %v",
i, claim.Position.ToGIndex(), claim.Position.TraceIndex(game.MaxDepth()), claim.Position.Depth(), claim.Position.IndexAtDepth(), claim.ParentContractIndex, claim.Value, claim.Claimant, claim.CounteredBy)
}
}
func applyActions(game types.Game, claimant common.Address, actions []types.Action) types.Game {
claims := game.Claims()
for _, action := range actions {
switch action.Type {
case types.ActionTypeMove:
newPosition := action.ParentPosition.Attack()
if !action.IsAttack {
newPosition = action.ParentPosition.Defend()
}
claim := types.Claim{
ClaimData: types.ClaimData{
Value: action.Value,
Bond: big.NewInt(0),
Position: newPosition,
},
Claimant: claimant,
Clock: nil,
ContractIndex: len(claims),
ParentContractIndex: action.ParentIdx,
}
claims = append(claims, claim)
case types.ActionTypeStep:
counteredClaim := claims[action.ParentIdx]
counteredClaim.CounteredBy = claimant
claims[action.ParentIdx] = counteredClaim
default:
panic(fmt.Errorf("unknown move type: %v", action.Type))
}
}
return types.NewGameState(claims, game.MaxDepth())
}
func gameResult(game types.Game) gameTypes.GameStatus {
tree := transform.CreateBidirectionalTree(game.Claims())
return resolution.Resolve(tree)
}
...@@ -10,6 +10,8 @@ import ( ...@@ -10,6 +10,8 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
var DefaultClaimant = common.Address{0x09, 0x23, 0x34, 0x45, 0x13, 0xb3}
// ClaimBuilder is a test utility to enable creating claims in a wide range of situations // ClaimBuilder is a test utility to enable creating claims in a wide range of situations
type ClaimBuilder struct { type ClaimBuilder struct {
require *require.Assertions require *require.Assertions
...@@ -80,6 +82,7 @@ func (c *ClaimBuilder) CreateRootClaim(correct bool) types.Claim { ...@@ -80,6 +82,7 @@ func (c *ClaimBuilder) CreateRootClaim(correct bool) types.Claim {
Value: value, Value: value,
Position: types.NewPosition(0, common.Big0), Position: types.NewPosition(0, common.Big0),
}, },
Claimant: DefaultClaimant,
} }
return claim return claim
} }
...@@ -91,6 +94,7 @@ func (c *ClaimBuilder) CreateLeafClaim(traceIndex *big.Int, correct bool) types. ...@@ -91,6 +94,7 @@ func (c *ClaimBuilder) CreateLeafClaim(traceIndex *big.Int, correct bool) types.
Value: c.claim(pos, correct), Value: c.claim(pos, correct),
Position: pos, Position: pos,
}, },
Claimant: DefaultClaimant,
} }
} }
...@@ -102,6 +106,7 @@ func (c *ClaimBuilder) AttackClaim(claim types.Claim, correct bool) types.Claim ...@@ -102,6 +106,7 @@ func (c *ClaimBuilder) AttackClaim(claim types.Claim, correct bool) types.Claim
Position: pos, Position: pos,
}, },
ParentContractIndex: claim.ContractIndex, ParentContractIndex: claim.ContractIndex,
Claimant: DefaultClaimant,
} }
} }
...@@ -113,6 +118,7 @@ func (c *ClaimBuilder) AttackClaimWithValue(claim types.Claim, value common.Hash ...@@ -113,6 +118,7 @@ func (c *ClaimBuilder) AttackClaimWithValue(claim types.Claim, value common.Hash
Position: pos, Position: pos,
}, },
ParentContractIndex: claim.ContractIndex, ParentContractIndex: claim.ContractIndex,
Claimant: DefaultClaimant,
} }
} }
...@@ -124,6 +130,7 @@ func (c *ClaimBuilder) DefendClaim(claim types.Claim, correct bool) types.Claim ...@@ -124,6 +130,7 @@ func (c *ClaimBuilder) DefendClaim(claim types.Claim, correct bool) types.Claim
Position: pos, Position: pos,
}, },
ParentContractIndex: claim.ContractIndex, ParentContractIndex: claim.ContractIndex,
Claimant: DefaultClaimant,
} }
} }
...@@ -135,5 +142,6 @@ func (c *ClaimBuilder) DefendClaimWithValue(claim types.Claim, value common.Hash ...@@ -135,5 +142,6 @@ func (c *ClaimBuilder) DefendClaimWithValue(claim types.Claim, value common.Hash
Position: pos, Position: pos,
}, },
ParentContractIndex: claim.ContractIndex, ParentContractIndex: claim.ContractIndex,
Claimant: DefaultClaimant,
} }
} }
...@@ -86,6 +86,12 @@ func (s *GameBuilderSeq) Defend(value common.Hash) *GameBuilderSeq { ...@@ -86,6 +86,12 @@ func (s *GameBuilderSeq) Defend(value common.Hash) *GameBuilderSeq {
} }
} }
func (s *GameBuilderSeq) Step() {
claims := s.gameBuilder.Game.Claims()
claims[len(claims)-1].CounteredBy = DefaultClaimant
s.gameBuilder.Game = types.NewGameState(claims, s.builder.maxDepth)
}
func (s *GameBuilderSeq) ExpectAttack() *GameBuilderSeq { func (s *GameBuilderSeq) ExpectAttack() *GameBuilderSeq {
newPos := s.lastClaim.Position.Attack() newPos := s.lastClaim.Position.Attack()
value := s.builder.CorrectClaimAtPosition(newPos) value := s.builder.CorrectClaimAtPosition(newPos)
......
...@@ -296,7 +296,6 @@ func createDeepClaimList() []faultTypes.Claim { ...@@ -296,7 +296,6 @@ func createDeepClaimList() []faultTypes.Claim {
Position: faultTypes.NewPosition(0, big.NewInt(0)), Position: faultTypes.NewPosition(0, big.NewInt(0)),
}, },
ContractIndex: 0, ContractIndex: 0,
CounteredBy: common.HexToAddress("0x222222"),
ParentContractIndex: math.MaxInt64, ParentContractIndex: math.MaxInt64,
Claimant: common.HexToAddress("0x111111"), Claimant: common.HexToAddress("0x111111"),
}, },
...@@ -304,7 +303,6 @@ func createDeepClaimList() []faultTypes.Claim { ...@@ -304,7 +303,6 @@ func createDeepClaimList() []faultTypes.Claim {
ClaimData: faultTypes.ClaimData{ ClaimData: faultTypes.ClaimData{
Position: faultTypes.NewPosition(1, big.NewInt(0)), Position: faultTypes.NewPosition(1, big.NewInt(0)),
}, },
CounteredBy: common.HexToAddress("0x111111"),
ContractIndex: 1, ContractIndex: 1,
ParentContractIndex: 0, ParentContractIndex: 0,
Claimant: common.HexToAddress("0x222222"), Claimant: common.HexToAddress("0x222222"),
......
...@@ -13,7 +13,7 @@ import ( ...@@ -13,7 +13,7 @@ import (
func Resolve(tree *monTypes.BidirectionalTree) gameTypes.GameStatus { func Resolve(tree *monTypes.BidirectionalTree) gameTypes.GameStatus {
for i := len(tree.Claims) - 1; i >= 0; i-- { for i := len(tree.Claims) - 1; i >= 0; i-- {
claim := tree.Claims[i] claim := tree.Claims[i]
counterClaimant := common.Address{} counterClaimant := claim.Claim.CounteredBy
for _, child := range claim.Children { for _, child := range claim.Children {
if child.Claim.CounteredBy == (common.Address{}) { if child.Claim.CounteredBy == (common.Address{}) {
counterClaimant = child.Claim.Claimant counterClaimant = child.Claim.Claimant
......
package resolution package resolution
import ( import (
"math"
"math/big" "math/big"
"testing" "testing"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/test"
"github.com/ethereum-optimism/optimism/op-dispute-mon/mon/transform"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
faultTypes "github.com/ethereum-optimism/optimism/op-challenger/game/fault/types" faultTypes "github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
gameTypes "github.com/ethereum-optimism/optimism/op-challenger/game/types" gameTypes "github.com/ethereum-optimism/optimism/op-challenger/game/types"
monTypes "github.com/ethereum-optimism/optimism/op-dispute-mon/mon/types"
) )
func TestResolver_Resolve(t *testing.T) { func TestResolver_Resolve(t *testing.T) {
t.Run("NoClaims", func(t *testing.T) { t.Run("NoClaims", func(t *testing.T) {
tree := &monTypes.BidirectionalTree{Claims: []*monTypes.BidirectionalClaim{}} tree := transform.CreateBidirectionalTree([]faultTypes.Claim{})
status := Resolve(tree) status := Resolve(tree)
require.Equal(t, gameTypes.GameStatusDefenderWon, status) require.Equal(t, gameTypes.GameStatusDefenderWon, status)
}) })
t.Run("SingleRootClaim", func(t *testing.T) { t.Run("SingleRootClaim", func(t *testing.T) {
tree := createBidirectionalTree(1) builder := test.NewAlphabetClaimBuilder(t, big.NewInt(10), 4).GameBuilder(true)
tree := transform.CreateBidirectionalTree(builder.Game.Claims())
tree.Claims[0].Claim.CounteredBy = common.Address{} tree.Claims[0].Claim.CounteredBy = common.Address{}
status := Resolve(tree) status := Resolve(tree)
require.Equal(t, gameTypes.GameStatusDefenderWon, status) require.Equal(t, gameTypes.GameStatusDefenderWon, status)
}) })
t.Run("ManyClaims_ChallengerWon", func(t *testing.T) { t.Run("ManyClaims_ChallengerWon", func(t *testing.T) {
tree := createBidirectionalTree(2) builder := test.NewAlphabetClaimBuilder(t, big.NewInt(10), 5).GameBuilder(true)
tree.Claims[1].Claim.CounteredBy = common.Address{} builder.Seq(). // Defender winning
tree.Claims[1].Children = make([]*monTypes.BidirectionalClaim, 0) AttackCorrect(). // Challenger winning
AttackCorrect(). // Defender winning
DefendCorrect(). // Challenger winning
DefendCorrect(). // Defender winning
AttackCorrect() // Challenger winning
tree := transform.CreateBidirectionalTree(builder.Game.Claims())
status := Resolve(tree) status := Resolve(tree)
require.Equal(t, gameTypes.GameStatusChallengerWon, status) require.Equal(t, gameTypes.GameStatusChallengerWon, status)
}) })
t.Run("ManyClaims_DefenderWon", func(t *testing.T) { t.Run("ManyClaims_DefenderWon", func(t *testing.T) {
status := Resolve(createBidirectionalTree(3)) builder := test.NewAlphabetClaimBuilder(t, big.NewInt(10), 5).GameBuilder(true)
builder.Seq(). // Defender winning
AttackCorrect(). // Challenger winning
AttackCorrect(). // Defender winning
DefendCorrect(). // Challenger winning
DefendCorrect() // Defender winning
tree := transform.CreateBidirectionalTree(builder.Game.Claims())
status := Resolve(tree)
require.Equal(t, gameTypes.GameStatusDefenderWon, status) require.Equal(t, gameTypes.GameStatusDefenderWon, status)
}) })
}
func createBidirectionalTree(claimCount uint64) *monTypes.BidirectionalTree { t.Run("MultipleBranches_ChallengerWon", func(t *testing.T) {
claimList := createDeepClaimList() builder := test.NewAlphabetClaimBuilder(t, big.NewInt(10), 5).GameBuilder(true)
bidirectionalClaimList := make([]*monTypes.BidirectionalClaim, len(claimList)) forkPoint := builder.Seq(). // Defender winning
bidirectionalClaimList[2] = &monTypes.BidirectionalClaim{ AttackCorrect(). // Challenger winning
Claim: &claimList[2], AttackCorrect() // Defender winning
Children: make([]*monTypes.BidirectionalClaim, 0), forkPoint.
} DefendCorrect(). // Challenger winning
bidirectionalClaimList[1] = &monTypes.BidirectionalClaim{ DefendCorrect(). // Defender winning
Claim: &claimList[1], AttackCorrect() // Challenger winning
Children: []*monTypes.BidirectionalClaim{bidirectionalClaimList[2]}, forkPoint.Defend(common.Hash{0xbb}). // Challenger winning
} DefendCorrect(). // Defender winning
bidirectionalClaimList[0] = &monTypes.BidirectionalClaim{ AttackCorrect(). // Challenger winning
Claim: &claimList[0], Step() // Defender winning
Children: []*monTypes.BidirectionalClaim{bidirectionalClaimList[1]}, forkPoint.Defend(common.Hash{0xcc}). // Challenger winning
} DefendCorrect() // Defender winning
return &monTypes.BidirectionalTree{Claims: bidirectionalClaimList[:claimCount]} tree := transform.CreateBidirectionalTree(builder.Game.Claims())
} status := Resolve(tree)
// First fork has an uncountered claim with challenger winning so that invalidates the parent and wins the game
require.Equal(t, gameTypes.GameStatusChallengerWon, status)
})
func createDeepClaimList() []faultTypes.Claim { t.Run("MultipleBranches_DefenderWon", func(t *testing.T) {
return []faultTypes.Claim{ builder := test.NewAlphabetClaimBuilder(t, big.NewInt(10), 5).GameBuilder(true)
{ forkPoint := builder.Seq(). // Defender winning
ClaimData: faultTypes.ClaimData{ AttackCorrect(). // Challenger winning
Position: faultTypes.NewPosition(0, big.NewInt(0)), AttackCorrect() // Defender winning
}, forkPoint.
ContractIndex: 0, DefendCorrect(). // Challenger winning
CounteredBy: common.HexToAddress("0x222222"), DefendCorrect() // Defender winning
ParentContractIndex: math.MaxInt64, forkPoint.Defend(common.Hash{0xbb}). // Challenger winning
Claimant: common.HexToAddress("0x111111"), DefendCorrect(). // Defender winning
}, AttackCorrect(). // Challenger winning
{ Step() // Defender winning
ClaimData: faultTypes.ClaimData{ forkPoint.Defend(common.Hash{0xcc}). // Challenger winning
Position: faultTypes.NewPosition(1, big.NewInt(0)), DefendCorrect() // Defender winning
}, tree := transform.CreateBidirectionalTree(builder.Game.Claims())
CounteredBy: common.HexToAddress("0x111111"), status := Resolve(tree)
ContractIndex: 1, // Defender won all forks
ParentContractIndex: 0, require.Equal(t, gameTypes.GameStatusDefenderWon, status)
Claimant: common.HexToAddress("0x222222"), })
},
{ t.Run("SteppedClaimed_ChallengerWon", func(t *testing.T) {
ClaimData: faultTypes.ClaimData{ builder := test.NewAlphabetClaimBuilder(t, big.NewInt(10), 4).GameBuilder(true)
Position: faultTypes.NewPosition(2, big.NewInt(0)), builder.Seq(). // Defender winning
}, AttackCorrect(). // Challenger winning
ContractIndex: 2, AttackCorrect(). // Defender winning
ParentContractIndex: 1, DefendCorrect(). // Challenger winning
Claimant: common.HexToAddress("0x111111"), DefendCorrect(). // Defender winning
}, Step() // Challenger winning
} claims := builder.Game.Claims()
// Successful step so mark as countered
claims[len(claims)-1].CounteredBy = common.Address{0xaa}
tree := transform.CreateBidirectionalTree(claims)
status := Resolve(tree)
require.Equal(t, gameTypes.GameStatusChallengerWon, status)
})
t.Run("SteppedClaimed_DefenderWon", func(t *testing.T) {
builder := test.NewAlphabetClaimBuilder(t, big.NewInt(10), 5).GameBuilder(true)
builder.Seq(). // Defender winning
AttackCorrect(). // Challenger winning
AttackCorrect(). // Defender winning
DefendCorrect(). // Challenger winning
DefendCorrect(). // Defender winning
AttackCorrect(). // Challenger winning
Step() // Defender winning
claims := builder.Game.Claims()
// Successful step so mark as countered
claims[len(claims)-1].CounteredBy = common.Address{0xaa}
tree := transform.CreateBidirectionalTree(claims)
status := Resolve(tree)
require.Equal(t, gameTypes.GameStatusDefenderWon, status)
})
} }
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