Commit 78e1084e authored by Inphi's avatar Inphi Committed by GitHub

dispute-game: Sanity check disputed claim index (#10520)

* dispute-game: Sanity check disputed claim index

* Fix tests

* update snapshots

* fix docs

* move param

* op-challenger: Supply parent value when performing moves

Also moves calculation of the required bond into the contract bindings so the returned TxCandidate is ready to send.

* op-challenger: Add backwards compatibility with v1.1.1

* op-e2e: Update e2e for new FaultDisputeGame ABI

---------
Co-authored-by: default avatarAdrian Sutton <adrian@oplabs.co>
parent 5e5d3f70
This diff is collapsed.
......@@ -51,14 +51,18 @@ func Move(ctx *cli.Context) error {
return fmt.Errorf("failed to create dispute game bindings: %w", err)
}
parentClaim, err := contract.GetClaim(ctx.Context, parentIndex)
if err != nil {
return fmt.Errorf("failed to get parent claim: %w", err)
}
var tx txmgr.TxCandidate
if attack {
tx, err = contract.AttackTx(parentIndex, claim)
tx, err = contract.AttackTx(ctx.Context, parentClaim, claim)
if err != nil {
return fmt.Errorf("failed to create attack tx: %w", err)
}
} else if defend {
tx, err = contract.DefendTx(parentIndex, claim)
tx, err = contract.DefendTx(ctx.Context, parentClaim, claim)
if err != nil {
return fmt.Errorf("failed to create defense tx: %w", err)
}
......
......@@ -120,7 +120,7 @@ func (a *Agent) performAction(ctx context.Context, wg *sync.WaitGroup, action ty
isLocal := containsOracleData && action.OracleData.IsLocal
actionLog = actionLog.New(
"is_attack", action.IsAttack,
"parent", action.ParentIdx,
"parent", action.ParentClaim.ContractIndex,
"prestate", common.Bytes2Hex(action.PreState),
"proof", common.Bytes2Hex(action.ProofData),
"containsOracleData", containsOracleData,
......@@ -130,7 +130,7 @@ func (a *Agent) performAction(ctx context.Context, wg *sync.WaitGroup, action ty
actionLog = actionLog.New("oracleKey", common.Bytes2Hex(action.OracleData.OracleKey))
}
} else if action.Type == types.ActionTypeMove {
actionLog = actionLog.New("is_attack", action.IsAttack, "parent", action.ParentIdx, "value", action.Value)
actionLog = actionLog.New("is_attack", action.IsAttack, "parent", action.ParentClaim.ContractIndex, "value", action.Value)
}
switch action.Type {
......
......@@ -109,6 +109,16 @@ func NewFaultDisputeGameContract(ctx context.Context, metrics metrics.ContractMe
contract: batching.NewBoundContract(legacyAbi, addr),
},
}, nil
} else if strings.HasPrefix(version, "1.1.") {
// Detected an older version of contracts, use a compatibility shim.
legacyAbi := mustParseAbi(faultDisputeGameAbi111)
return &FaultDisputeGameContract111{
FaultDisputeGameContractLatest: FaultDisputeGameContractLatest{
metrics: metrics,
multiCaller: caller,
contract: batching.NewBoundContract(legacyAbi, addr),
},
}, nil
} else {
return &FaultDisputeGameContractLatest{
metrics: metrics,
......@@ -480,14 +490,26 @@ func (f *FaultDisputeGameContractLatest) ChallengeL2BlockNumberTx(challenge *typ
}, headerRlp).ToTxCandidate()
}
func (f *FaultDisputeGameContractLatest) AttackTx(parentContractIndex uint64, pivot common.Hash) (txmgr.TxCandidate, error) {
call := f.contract.Call(methodAttack, new(big.Int).SetUint64(parentContractIndex), pivot)
return call.ToTxCandidate()
func (f *FaultDisputeGameContractLatest) AttackTx(ctx context.Context, parent types.Claim, pivot common.Hash) (txmgr.TxCandidate, error) {
call := f.contract.Call(methodAttack, parent.Value, big.NewInt(int64(parent.ContractIndex)), pivot)
return f.txWithBond(ctx, parent.Position.Attack(), call)
}
func (f *FaultDisputeGameContractLatest) DefendTx(parentContractIndex uint64, pivot common.Hash) (txmgr.TxCandidate, error) {
call := f.contract.Call(methodDefend, new(big.Int).SetUint64(parentContractIndex), pivot)
return call.ToTxCandidate()
func (f *FaultDisputeGameContractLatest) DefendTx(ctx context.Context, parent types.Claim, pivot common.Hash) (txmgr.TxCandidate, error) {
call := f.contract.Call(methodDefend, parent.Value, big.NewInt(int64(parent.ContractIndex)), pivot)
return f.txWithBond(ctx, parent.Position.Defend(), call)
}
func (f *FaultDisputeGameContractLatest) txWithBond(ctx context.Context, position types.Position, call *batching.ContractCall) (txmgr.TxCandidate, error) {
tx, err := call.ToTxCandidate()
if err != nil {
return txmgr.TxCandidate{}, fmt.Errorf("failed to create transaction: %w", err)
}
tx.Value, err = f.GetRequiredBond(ctx, position)
if err != nil {
return txmgr.TxCandidate{}, fmt.Errorf("failed to fetch required bond: %w", err)
}
return tx, nil
}
func (f *FaultDisputeGameContractLatest) StepTx(claimIdx uint64, isAttack bool, stateData []byte, proof []byte) (txmgr.TxCandidate, error) {
......@@ -595,8 +617,8 @@ type FaultDisputeGameContract interface {
IsResolved(ctx context.Context, block rpcblock.Block, claims ...types.Claim) ([]bool, error)
IsL2BlockNumberChallenged(ctx context.Context, block rpcblock.Block) (bool, error)
ChallengeL2BlockNumberTx(challenge *types.InvalidL2BlockNumberChallenge) (txmgr.TxCandidate, error)
AttackTx(parentContractIndex uint64, pivot common.Hash) (txmgr.TxCandidate, error)
DefendTx(parentContractIndex uint64, pivot common.Hash) (txmgr.TxCandidate, error)
AttackTx(ctx context.Context, parent types.Claim, pivot common.Hash) (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)
CallResolveClaim(ctx context.Context, claimIdx uint64) error
ResolveClaimTx(claimIdx uint64) (txmgr.TxCandidate, error)
......
......@@ -4,11 +4,13 @@ import (
"context"
_ "embed"
"fmt"
"math/big"
"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-service/sources/batching/rpcblock"
"github.com/ethereum-optimism/optimism/op-service/txmgr"
"github.com/ethereum/go-ethereum/common"
)
//go:embed abis/FaultDisputeGame-0.18.1.json
......@@ -59,3 +61,13 @@ func (f *FaultDisputeGameContract0180) IsL2BlockNumberChallenged(_ context.Conte
func (f *FaultDisputeGameContract0180) ChallengeL2BlockNumberTx(_ *types.InvalidL2BlockNumberChallenge) (txmgr.TxCandidate, error) {
return txmgr.TxCandidate{}, ErrChallengeL2BlockNotSupported
}
func (f *FaultDisputeGameContract0180) AttackTx(ctx context.Context, parent types.Claim, pivot common.Hash) (txmgr.TxCandidate, error) {
call := f.contract.Call(methodAttack, big.NewInt(int64(parent.ContractIndex)), pivot)
return f.txWithBond(ctx, parent.Position.Attack(), call)
}
func (f *FaultDisputeGameContract0180) DefendTx(ctx context.Context, parent types.Claim, pivot common.Hash) (txmgr.TxCandidate, error) {
call := f.contract.Call(methodDefend, big.NewInt(int64(parent.ContractIndex)), pivot)
return f.txWithBond(ctx, parent.Position.Defend(), call)
}
......@@ -12,6 +12,7 @@ import (
"github.com/ethereum-optimism/optimism/op-service/sources/batching"
"github.com/ethereum-optimism/optimism/op-service/sources/batching/rpcblock"
"github.com/ethereum-optimism/optimism/op-service/txmgr"
"github.com/ethereum/go-ethereum/common"
)
//go:embed abis/FaultDisputeGame-0.8.0.json
......@@ -146,3 +147,13 @@ func (f *FaultDisputeGameContract080) IsL2BlockNumberChallenged(_ context.Contex
func (f *FaultDisputeGameContract080) ChallengeL2BlockNumberTx(_ *types.InvalidL2BlockNumberChallenge) (txmgr.TxCandidate, error) {
return txmgr.TxCandidate{}, ErrChallengeL2BlockNotSupported
}
func (f *FaultDisputeGameContract080) AttackTx(ctx context.Context, parent types.Claim, pivot common.Hash) (txmgr.TxCandidate, error) {
call := f.contract.Call(methodAttack, big.NewInt(int64(parent.ContractIndex)), pivot)
return f.txWithBond(ctx, parent.Position.Attack(), call)
}
func (f *FaultDisputeGameContract080) DefendTx(ctx context.Context, parent types.Claim, pivot common.Hash) (txmgr.TxCandidate, error) {
call := f.contract.Call(methodDefend, big.NewInt(int64(parent.ContractIndex)), pivot)
return f.txWithBond(ctx, parent.Position.Defend(), call)
}
package contracts
import (
"context"
_ "embed"
"math/big"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
"github.com/ethereum-optimism/optimism/op-service/txmgr"
"github.com/ethereum/go-ethereum/common"
)
//go:embed abis/FaultDisputeGame-1.1.1.json
var faultDisputeGameAbi111 []byte
type FaultDisputeGameContract111 struct {
FaultDisputeGameContractLatest
}
func (f *FaultDisputeGameContract111) AttackTx(ctx context.Context, parent types.Claim, pivot common.Hash) (txmgr.TxCandidate, error) {
call := f.contract.Call(methodAttack, big.NewInt(int64(parent.ContractIndex)), pivot)
return f.txWithBond(ctx, parent.Position.Attack(), call)
}
func (f *FaultDisputeGameContract111) DefendTx(ctx context.Context, parent types.Claim, pivot common.Hash) (txmgr.TxCandidate, error) {
call := f.contract.Call(methodDefend, big.NewInt(int64(parent.ContractIndex)), pivot)
return f.txWithBond(ctx, parent.Position.Defend(), call)
}
......@@ -7,6 +7,7 @@ import (
"math"
"math/big"
"math/rand"
"slices"
"testing"
"time"
......@@ -37,10 +38,15 @@ type contractVersion struct {
loadAbi func() *abi.ABI
}
func (c contractVersion) Is(versions ...string) bool {
return slices.Contains(versions, c.version)
}
const (
vers080 = "0.8.0"
vers0180 = "0.18.0"
versLatest = "1.1.0"
vers111 = "1.1.1"
versLatest = "1.2.0"
)
var versions = []contractVersion{
......@@ -56,6 +62,12 @@ var versions = []contractVersion{
return mustParseAbi(faultDisputeGameAbi0180)
},
},
{
version: vers111,
loadAbi: func() *abi.ABI {
return mustParseAbi(faultDisputeGameAbi111)
},
},
{
version: versLatest,
loadAbi: snapshots.LoadFaultDisputeGameABI,
......@@ -379,11 +391,19 @@ func TestAttackTx(t *testing.T) {
version := version
t.Run(version.version, func(t *testing.T) {
stubRpc, game := setupFaultDisputeGameTest(t, version)
bond := big.NewInt(1044)
value := common.Hash{0xaa}
parent := faultTypes.Claim{ClaimData: faultTypes.ClaimData{Value: common.Hash{0xbb}}, ContractIndex: 111}
stubRpc.SetResponse(fdgAddr, methodRequiredBond, rpcblock.Latest, []interface{}{parent.Position.Attack().ToGIndex()}, []interface{}{bond})
if version.Is(vers080, vers0180, vers111) {
stubRpc.SetResponse(fdgAddr, methodAttack, rpcblock.Latest, []interface{}{big.NewInt(111), value}, nil)
tx, err := game.AttackTx(111, value)
} else {
stubRpc.SetResponse(fdgAddr, methodAttack, rpcblock.Latest, []interface{}{parent.Value, big.NewInt(111), value}, nil)
}
tx, err := game.AttackTx(context.Background(), parent, value)
require.NoError(t, err)
stubRpc.VerifyTxCandidate(tx)
require.Equal(t, bond, tx.Value)
})
}
}
......@@ -393,11 +413,19 @@ func TestDefendTx(t *testing.T) {
version := version
t.Run(version.version, func(t *testing.T) {
stubRpc, game := setupFaultDisputeGameTest(t, version)
bond := big.NewInt(1044)
value := common.Hash{0xaa}
parent := faultTypes.Claim{ClaimData: faultTypes.ClaimData{Value: common.Hash{0xbb}}, ContractIndex: 111}
stubRpc.SetResponse(fdgAddr, methodRequiredBond, rpcblock.Latest, []interface{}{parent.Position.Defend().ToGIndex()}, []interface{}{bond})
if version.Is(vers080, vers0180, vers111) {
stubRpc.SetResponse(fdgAddr, methodDefend, rpcblock.Latest, []interface{}{big.NewInt(111), value}, nil)
tx, err := game.DefendTx(111, value)
} else {
stubRpc.SetResponse(fdgAddr, methodDefend, rpcblock.Latest, []interface{}{parent.Value, big.NewInt(111), value}, nil)
}
tx, err := game.DefendTx(context.Background(), parent, value)
require.NoError(t, err)
stubRpc.VerifyTxCandidate(tx)
require.Equal(t, bond, tx.Value)
})
}
}
......
......@@ -4,7 +4,6 @@ import (
"context"
"errors"
"fmt"
"math/big"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/preimages"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
......@@ -19,11 +18,10 @@ type GameContract interface {
ResolveTx() (txmgr.TxCandidate, error)
CallResolveClaim(ctx context.Context, claimIdx uint64) error
ResolveClaimTx(claimIdx uint64) (txmgr.TxCandidate, error)
AttackTx(parentContractIndex uint64, pivot common.Hash) (txmgr.TxCandidate, error)
DefendTx(parentContractIndex uint64, pivot common.Hash) (txmgr.TxCandidate, error)
AttackTx(ctx context.Context, parent types.Claim, pivot common.Hash) (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)
ChallengeL2BlockNumberTx(challenge *types.InvalidL2BlockNumberChallenge) (txmgr.TxCandidate, error)
GetRequiredBond(ctx context.Context, position types.Position) (*big.Int, error)
}
type Oracle interface {
......@@ -101,7 +99,7 @@ func (r *FaultResponder) PerformAction(ctx context.Context, action types.Action)
}
// Always upload local preimages
if !preimageExists {
err := r.uploader.UploadPreimage(ctx, uint64(action.ParentIdx), action.OracleData)
err := r.uploader.UploadPreimage(ctx, uint64(action.ParentClaim.ContractIndex), action.OracleData)
if errors.Is(err, preimages.ErrChallengePeriodNotOver) {
r.log.Debug("Large Preimage Squeeze failed, challenge period not over")
return nil
......@@ -114,22 +112,13 @@ func (r *FaultResponder) PerformAction(ctx context.Context, action types.Action)
var err error
switch action.Type {
case types.ActionTypeMove:
var movePos types.Position
if action.IsAttack {
movePos = action.ParentPosition.Attack()
candidate, err = r.contract.AttackTx(uint64(action.ParentIdx), action.Value)
candidate, err = r.contract.AttackTx(ctx, action.ParentClaim, action.Value)
} else {
movePos = action.ParentPosition.Defend()
candidate, err = r.contract.DefendTx(uint64(action.ParentIdx), action.Value)
candidate, err = r.contract.DefendTx(ctx, action.ParentClaim, action.Value)
}
bondValue, err := r.contract.GetRequiredBond(ctx, movePos)
if err != nil {
return err
}
candidate.Value = bondValue
case types.ActionTypeStep:
candidate, err = r.contract.StepTx(uint64(action.ParentIdx), action.IsAttack, action.PreState, action.ProofData)
candidate, err = r.contract.StepTx(uint64(action.ParentClaim.ContractIndex), action.IsAttack, action.PreState, action.ProofData)
case types.ActionTypeChallengeL2BlockNumber:
candidate, err = r.contract.ChallengeL2BlockNumberTx(action.InvalidL2BlockNumberChallenge)
}
......
......@@ -110,7 +110,7 @@ func TestPerformAction(t *testing.T) {
mockTxMgr.sendFails = true
err := responder.PerformAction(context.Background(), types.Action{
Type: types.ActionTypeMove,
ParentIdx: 123,
ParentClaim: types.Claim{ContractIndex: 123},
IsAttack: true,
Value: common.Hash{0xaa},
})
......@@ -122,7 +122,7 @@ func TestPerformAction(t *testing.T) {
responder, mockTxMgr, _, _, _ := newTestFaultResponder(t)
err := responder.PerformAction(context.Background(), types.Action{
Type: types.ActionTypeMove,
ParentIdx: 123,
ParentClaim: types.Claim{ContractIndex: 123},
IsAttack: true,
Value: common.Hash{0xaa},
})
......@@ -134,7 +134,7 @@ func TestPerformAction(t *testing.T) {
responder, mockTxMgr, contract, _, _ := newTestFaultResponder(t)
action := types.Action{
Type: types.ActionTypeMove,
ParentIdx: 123,
ParentClaim: types.Claim{ContractIndex: 123},
IsAttack: true,
Value: common.Hash{0xaa},
}
......@@ -142,7 +142,7 @@ func TestPerformAction(t *testing.T) {
require.NoError(t, err)
require.Len(t, mockTxMgr.sent, 1)
require.EqualValues(t, []interface{}{uint64(action.ParentIdx), action.Value}, contract.attackArgs)
require.EqualValues(t, []interface{}{action.ParentClaim, action.Value}, contract.attackArgs)
require.Equal(t, ([]byte)("attack"), mockTxMgr.sent[0].TxData)
})
......@@ -150,7 +150,7 @@ func TestPerformAction(t *testing.T) {
responder, mockTxMgr, contract, _, _ := newTestFaultResponder(t)
action := types.Action{
Type: types.ActionTypeMove,
ParentIdx: 123,
ParentClaim: types.Claim{ContractIndex: 123},
IsAttack: false,
Value: common.Hash{0xaa},
}
......@@ -158,7 +158,7 @@ func TestPerformAction(t *testing.T) {
require.NoError(t, err)
require.Len(t, mockTxMgr.sent, 1)
require.EqualValues(t, []interface{}{uint64(action.ParentIdx), action.Value}, contract.defendArgs)
require.EqualValues(t, []interface{}{action.ParentClaim, action.Value}, contract.defendArgs)
require.Equal(t, ([]byte)("defend"), mockTxMgr.sent[0].TxData)
})
......@@ -166,7 +166,7 @@ func TestPerformAction(t *testing.T) {
responder, mockTxMgr, contract, _, _ := newTestFaultResponder(t)
action := types.Action{
Type: types.ActionTypeStep,
ParentIdx: 123,
ParentClaim: types.Claim{ContractIndex: 123},
IsAttack: true,
PreState: []byte{1, 2, 3},
ProofData: []byte{4, 5, 6},
......@@ -175,7 +175,7 @@ func TestPerformAction(t *testing.T) {
require.NoError(t, err)
require.Len(t, mockTxMgr.sent, 1)
require.EqualValues(t, []interface{}{uint64(action.ParentIdx), action.IsAttack, action.PreState, action.ProofData}, contract.stepArgs)
require.EqualValues(t, []interface{}{uint64(123), action.IsAttack, action.PreState, action.ProofData}, contract.stepArgs)
require.Equal(t, ([]byte)("step"), mockTxMgr.sent[0].TxData)
})
......@@ -183,7 +183,7 @@ func TestPerformAction(t *testing.T) {
responder, mockTxMgr, contract, uploader, oracle := newTestFaultResponder(t)
action := types.Action{
Type: types.ActionTypeStep,
ParentIdx: 123,
ParentClaim: types.Claim{ContractIndex: 123},
IsAttack: true,
PreState: []byte{1, 2, 3},
ProofData: []byte{4, 5, 6},
......@@ -205,7 +205,7 @@ func TestPerformAction(t *testing.T) {
responder, mockTxMgr, contract, uploader, oracle := newTestFaultResponder(t)
action := types.Action{
Type: types.ActionTypeStep,
ParentIdx: 123,
ParentClaim: types.Claim{ContractIndex: 123},
IsAttack: true,
PreState: []byte{1, 2, 3},
ProofData: []byte{4, 5, 6},
......@@ -228,7 +228,7 @@ func TestPerformAction(t *testing.T) {
uploader.uploadFails = true
action := types.Action{
Type: types.ActionTypeStep,
ParentIdx: 123,
ParentClaim: types.Claim{ContractIndex: 123},
IsAttack: true,
PreState: []byte{1, 2, 3},
ProofData: []byte{4, 5, 6},
......@@ -248,7 +248,7 @@ func TestPerformAction(t *testing.T) {
oracle.existsResult = true
action := types.Action{
Type: types.ActionTypeStep,
ParentIdx: 123,
ParentClaim: types.Claim{ContractIndex: 123},
IsAttack: true,
PreState: []byte{1, 2, 3},
ProofData: []byte{4, 5, 6},
......@@ -269,7 +269,7 @@ func TestPerformAction(t *testing.T) {
oracle.existsFails = true
action := types.Action{
Type: types.ActionTypeStep,
ParentIdx: 123,
ParentClaim: types.Claim{ContractIndex: 123},
IsAttack: true,
PreState: []byte{1, 2, 3},
ProofData: []byte{4, 5, 6},
......@@ -406,13 +406,13 @@ func (m *mockContract) ChallengeL2BlockNumberTx(challenge *types.InvalidL2BlockN
return txmgr.TxCandidate{TxData: ([]byte)("challenge")}, nil
}
func (m *mockContract) AttackTx(parentClaimId uint64, claim common.Hash) (txmgr.TxCandidate, error) {
m.attackArgs = []interface{}{parentClaimId, claim}
func (m *mockContract) AttackTx(_ context.Context, parent types.Claim, claim common.Hash) (txmgr.TxCandidate, error) {
m.attackArgs = []interface{}{parent, claim}
return txmgr.TxCandidate{TxData: ([]byte)("attack")}, nil
}
func (m *mockContract) DefendTx(parentClaimId uint64, claim common.Hash) (txmgr.TxCandidate, error) {
m.defendArgs = []interface{}{parentClaimId, claim}
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
}
......@@ -427,10 +427,6 @@ func (m *mockContract) UpdateOracleTx(_ context.Context, claimIdx uint64, data *
return txmgr.TxCandidate{TxData: ([]byte)("updateOracle")}, nil
}
func (m *mockContract) GetRequiredBond(_ context.Context, position types.Position) (*big.Int, error) {
return big.NewInt(5), nil
}
func (m *mockContract) GetCredit(_ context.Context, _ common.Address) (*big.Int, error) {
return big.NewInt(5), nil
}
......
......@@ -86,8 +86,7 @@ func (s *GameSolver) calculateStep(ctx context.Context, game types.Game, claim t
}
return &types.Action{
Type: types.ActionTypeStep,
ParentIdx: step.LeafClaim.ContractIndex,
ParentPosition: step.LeafClaim.Position,
ParentClaim: step.LeafClaim,
IsAttack: step.IsAttack,
PreState: step.PreState,
ProofData: step.ProofData,
......@@ -110,8 +109,7 @@ func (s *GameSolver) calculateMove(ctx context.Context, game types.Game, claim t
return &types.Action{
Type: types.ActionTypeMove,
IsAttack: !game.DefendsParent(*move),
ParentIdx: move.ParentContractIndex,
ParentPosition: claim.Position,
ParentClaim: game.Claims()[move.ParentContractIndex],
Value: move.Value,
}, nil
}
......@@ -209,7 +209,7 @@ func TestCalculateNextActions(t *testing.T) {
postState, actions := runStep(t, solver, game, claimBuilder.CorrectTraceProvider())
for i, action := range builder.ExpectedActions {
t.Logf("Expect %v: Type: %v, ParentIdx: %v, Attack: %v, Value: %v, PreState: %v, ProofData: %v",
i, action.Type, action.ParentIdx, action.IsAttack, action.Value, hex.EncodeToString(action.PreState), hex.EncodeToString(action.ProofData))
i, action.Type, action.ParentClaim.ContractIndex, action.IsAttack, action.Value, hex.EncodeToString(action.PreState), hex.EncodeToString(action.ProofData))
require.Containsf(t, actions, action, "Expected claim %v missing", i)
}
require.Len(t, actions, len(builder.ExpectedActions), "Incorrect number of actions")
......@@ -227,7 +227,7 @@ func runStep(t *testing.T, solver *GameSolver, game types.Game, correctTraceProv
for i, action := range actions {
t.Logf("Move %v: Type: %v, ParentIdx: %v, Attack: %v, Value: %v, PreState: %v, ProofData: %v",
i, action.Type, action.ParentIdx, action.IsAttack, action.Value, hex.EncodeToString(action.PreState), hex.EncodeToString(action.ProofData))
i, action.Type, action.ParentClaim.ContractIndex, action.IsAttack, action.Value, hex.EncodeToString(action.PreState), hex.EncodeToString(action.ProofData))
// Check that every move the solver returns meets the generic validation rules
require.NoError(t, checkRules(game, action, correctTraceProvider), "Attempting to perform invalid action")
}
......@@ -333,9 +333,9 @@ func applyActions(game types.Game, claimant common.Address, actions []types.Acti
for _, action := range actions {
switch action.Type {
case types.ActionTypeMove:
newPosition := action.ParentPosition.Attack()
newPosition := action.ParentClaim.Position.Attack()
if !action.IsAttack {
newPosition = action.ParentPosition.Defend()
newPosition = action.ParentClaim.Position.Defend()
}
claim := types.Claim{
ClaimData: types.ClaimData{
......@@ -345,13 +345,13 @@ func applyActions(game types.Game, claimant common.Address, actions []types.Acti
},
Claimant: claimant,
ContractIndex: len(claims),
ParentContractIndex: action.ParentIdx,
ParentContractIndex: action.ParentClaim.ContractIndex,
}
claims = append(claims, claim)
case types.ActionTypeStep:
counteredClaim := claims[action.ParentIdx]
counteredClaim := claims[action.ParentClaim.ContractIndex]
counteredClaim.CounteredBy = claimant
claims[action.ParentIdx] = counteredClaim
claims[action.ParentClaim.ContractIndex] = counteredClaim
default:
panic(fmt.Errorf("unknown move type: %v", action.Type))
}
......
......@@ -46,8 +46,8 @@ func checkRules(game types.Game, action types.Action, correctTrace types.TracePr
// parentMustExist checks that every action performed has a valid parent claim
// Rationale: The action would be rejected by the contracts
func parentMustExist(game types.Game, action types.Action, _ types.TraceProvider) error {
if len(game.Claims()) <= action.ParentIdx || action.ParentIdx < 0 {
return fmt.Errorf("parent claim %v does not exist in game with %v claims", action.ParentIdx, len(game.Claims()))
if len(game.Claims()) <= action.ParentClaim.ContractIndex || action.ParentClaim.ContractIndex < 0 {
return fmt.Errorf("parent claim %v does not exist in game with %v claims", action.ParentClaim.ContractIndex, len(game.Claims()))
}
return nil
}
......@@ -58,7 +58,7 @@ func onlyStepAtMaxDepth(game types.Game, action types.Action, _ types.TraceProvi
if action.Type == types.ActionTypeStep {
return nil
}
parentDepth := game.Claims()[action.ParentIdx].Position.Depth()
parentDepth := game.Claims()[action.ParentClaim.ContractIndex].Position.Depth()
if parentDepth >= game.MaxDepth() {
return fmt.Errorf("parent at max depth (%v) but attempting to perform %v action instead of step",
parentDepth, action.Type)
......@@ -72,7 +72,7 @@ func onlyMoveBeforeMaxDepth(game types.Game, action types.Action, _ types.TraceP
if action.Type == types.ActionTypeMove {
return nil
}
parentDepth := game.Claims()[action.ParentIdx].Position.Depth()
parentDepth := game.Claims()[action.ParentClaim.ContractIndex].Position.Depth()
if parentDepth < game.MaxDepth() {
return fmt.Errorf("parent (%v) not at max depth (%v) but attempting to perform %v action instead of move",
parentDepth, game.MaxDepth(), action.Type)
......@@ -87,7 +87,7 @@ func doNotDuplicateExistingMoves(game types.Game, action types.Action, _ types.T
Value: action.Value,
Position: resultingPosition(game, action),
}
if game.IsDuplicate(types.Claim{ClaimData: newClaimData, ParentContractIndex: action.ParentIdx}) {
if game.IsDuplicate(types.Claim{ClaimData: newClaimData, ParentContractIndex: action.ParentClaim.ContractIndex}) {
return fmt.Errorf("creating duplicate claim at %v with value %v", newClaimData.Position.ToGIndex(), newClaimData.Value)
}
return nil
......@@ -96,7 +96,7 @@ func doNotDuplicateExistingMoves(game types.Game, action types.Action, _ types.T
// doNotStepAlreadyCounteredClaims checks the challenger does not attempt to call step on already countered claims
// Rationale: The step call is redundant and a waste of gas
func doNotStepAlreadyCounteredClaims(game types.Game, action types.Action, _ types.TraceProvider) error {
claim := game.Claims()[action.ParentIdx]
claim := game.Claims()[action.ParentClaim.ContractIndex]
if claim.CounteredBy != (common.Address{}) {
return fmt.Errorf("attempting to step already countered claim: %v", claim.ContractIndex)
}
......@@ -106,8 +106,8 @@ func doNotStepAlreadyCounteredClaims(game types.Game, action types.Action, _ typ
// doNotDefendRootClaim checks the challenger doesn't attempt to defend the root claim
// Rationale: The action would be rejected by the contracts
func doNotDefendRootClaim(game types.Game, action types.Action, _ types.TraceProvider) error {
if game.Claims()[action.ParentIdx].IsRootPosition() && !action.IsAttack {
return fmt.Errorf("defending the root claim at idx %v", action.ParentIdx)
if game.Claims()[action.ParentClaim.ContractIndex].IsRootPosition() && !action.IsAttack {
return fmt.Errorf("defending the root claim at idx %v", action.ParentClaim.ContractIndex)
}
return nil
}
......@@ -115,9 +115,9 @@ func doNotDefendRootClaim(game types.Game, action types.Action, _ types.TracePro
// doNotCounterSelf checks the challenger doesn't counter its own claims
// Rationale: The challenger should not disagree with itself
func doNotCounterSelf(game types.Game, action types.Action, _ types.TraceProvider) error {
claim := game.Claims()[action.ParentIdx]
claim := game.Claims()[action.ParentClaim.ContractIndex]
if claim.Claimant == challengerAddr {
return fmt.Errorf("countering own claim at idx %v", action.ParentIdx)
return fmt.Errorf("countering own claim at idx %v", action.ParentClaim.ContractIndex)
}
return nil
}
......@@ -136,7 +136,7 @@ func avoidPoisonedPrestate(game types.Game, action types.Action, correctTrace ty
movePosition := resultingPosition(game, action)
honestTraceIndex := movePosition.TraceIndex(game.MaxDepth())
// Walk back up the claims and find the claim with highest trace index < honestTraceIndex
claim := game.Claims()[action.ParentIdx]
claim := game.Claims()[action.ParentClaim.ContractIndex]
var preStateClaim types.Claim
for {
ancestors += printClaim(claim, game) + "\n"
......@@ -164,7 +164,7 @@ func avoidPoisonedPrestate(game types.Game, action types.Action, correctTrace ty
return fmt.Errorf("failed to get correct trace at position %v: %w", preStateClaim.Position, err)
}
if correctValue != preStateClaim.Value {
err = fmt.Errorf("prestate poisoned claim %v has invalid prestate and is left of honest claim countering %v at trace index %v", preStateClaim.ContractIndex, action.ParentIdx, honestTraceIndex)
err = fmt.Errorf("prestate poisoned claim %v has invalid prestate and is left of honest claim countering %v at trace index %v", preStateClaim.ContractIndex, action.ParentClaim.ContractIndex, honestTraceIndex)
return err
}
return nil
......@@ -199,10 +199,10 @@ func detectFailedStep(game types.Game, action types.Action, correctTrace types.T
poststateIndex = new(big.Int).Add(honestTraceIndex, big.NewInt(1))
}
// Walk back up the claims and find the claim required post state index
claim := game.Claims()[action.ParentIdx]
claim := game.Claims()[action.ParentClaim.ContractIndex]
poststateClaim, ok := game.AncestorWithTraceIndex(claim, poststateIndex)
if !ok {
return fmt.Errorf("did not find required poststate at %v to counter claim %v", poststateIndex, action.ParentIdx)
return fmt.Errorf("did not find required poststate at %v to counter claim %v", poststateIndex, action.ParentClaim.ContractIndex)
}
correctValue, err := correctTrace.Get(context.Background(), poststateClaim.Position)
if err != nil {
......@@ -212,7 +212,7 @@ func detectFailedStep(game types.Game, action types.Action, correctTrace types.T
parentPostAgree := (claim.Depth()-poststateClaim.Depth())%2 == 0
if parentPostAgree == validStep {
return fmt.Errorf("failed step against claim at %v using poststate from claim %v post state is correct? %v parentPostAgree? %v",
action.ParentIdx, poststateClaim.ContractIndex, validStep, parentPostAgree)
action.ParentClaim.ContractIndex, poststateClaim.ContractIndex, validStep, parentPostAgree)
}
return nil
}
......@@ -242,7 +242,7 @@ func detectPoisonedStepPrestate(game types.Game, action types.Action, correctTra
return nil
}
// Walk back up the claims and find the claim with highest trace index < honestTraceIndex
claim := game.Claims()[action.ParentIdx]
claim := game.Claims()[action.ParentClaim.ContractIndex]
preStateClaim, ok := game.AncestorWithTraceIndex(claim, prestateIndex)
if !ok {
return fmt.Errorf("performing step against claim %v with no prestate available at %v", claim.ContractIndex, prestateIndex)
......@@ -253,9 +253,9 @@ func detectPoisonedStepPrestate(game types.Game, action types.Action, correctTra
}
if correctValue != preStateClaim.Value {
if action.Type == types.ActionTypeStep {
return fmt.Errorf("stepping from poisoned prestate at claim %v when countering %v", preStateClaim.ContractIndex, action.ParentIdx)
return fmt.Errorf("stepping from poisoned prestate at claim %v when countering %v", preStateClaim.ContractIndex, action.ParentClaim.ContractIndex)
} else {
return fmt.Errorf("posting leaf claim with poisoned prestate from claim %v when countering %v", preStateClaim.ContractIndex, action.ParentIdx)
return fmt.Errorf("posting leaf claim with poisoned prestate from claim %v when countering %v", preStateClaim.ContractIndex, action.ParentClaim.ContractIndex)
}
}
if action.Type == types.ActionTypeStep {
......@@ -268,7 +268,7 @@ func detectPoisonedStepPrestate(game types.Game, action types.Action, correctTra
}
func resultingPosition(game types.Game, action types.Action) types.Position {
parentPos := game.Claims()[action.ParentIdx].Position
parentPos := game.Claims()[action.ParentClaim.ContractIndex].Position
if action.Type == types.ActionTypeStep {
return parentPos
}
......
......@@ -103,8 +103,7 @@ func (s *GameBuilderSeq) ExpectAttack() *GameBuilderSeq {
value := s.builder.CorrectClaimAtPosition(newPos)
s.gameBuilder.ExpectedActions = append(s.gameBuilder.ExpectedActions, types.Action{
Type: types.ActionTypeMove,
ParentIdx: s.lastClaim.ContractIndex,
ParentPosition: s.lastClaim.Position,
ParentClaim: s.lastClaim,
IsAttack: true,
Value: value,
})
......@@ -116,8 +115,7 @@ func (s *GameBuilderSeq) ExpectDefend() *GameBuilderSeq {
value := s.builder.CorrectClaimAtPosition(newPos)
s.gameBuilder.ExpectedActions = append(s.gameBuilder.ExpectedActions, types.Action{
Type: types.ActionTypeMove,
ParentIdx: s.lastClaim.ContractIndex,
ParentPosition: s.lastClaim.Position,
ParentClaim: s.lastClaim,
IsAttack: false,
Value: value,
})
......@@ -128,8 +126,7 @@ func (s *GameBuilderSeq) ExpectStepAttack() *GameBuilderSeq {
traceIdx := s.lastClaim.TraceIndex(s.builder.maxDepth)
s.gameBuilder.ExpectedActions = append(s.gameBuilder.ExpectedActions, types.Action{
Type: types.ActionTypeStep,
ParentIdx: s.lastClaim.ContractIndex,
ParentPosition: s.lastClaim.Position,
ParentClaim: s.lastClaim,
IsAttack: true,
PreState: s.builder.CorrectPreState(traceIdx),
ProofData: s.builder.CorrectProofData(traceIdx),
......@@ -142,8 +139,7 @@ func (s *GameBuilderSeq) ExpectStepDefend() *GameBuilderSeq {
traceIdx := new(big.Int).Add(s.lastClaim.TraceIndex(s.builder.maxDepth), big.NewInt(1))
s.gameBuilder.ExpectedActions = append(s.gameBuilder.ExpectedActions, types.Action{
Type: types.ActionTypeStep,
ParentIdx: s.lastClaim.ContractIndex,
ParentPosition: s.lastClaim.Position,
ParentClaim: s.lastClaim,
IsAttack: false,
PreState: s.builder.CorrectPreState(traceIdx),
ProofData: s.builder.CorrectProofData(traceIdx),
......
......@@ -18,8 +18,7 @@ type Action struct {
Type ActionType
// Moves and Steps
ParentIdx int
ParentPosition Position
ParentClaim Claim
IsAttack bool
// Moves
......
......@@ -568,7 +568,7 @@ func (g *OutputGameHelper) Attack(ctx context.Context, claimIdx int64, claim com
transactOpts := g.makeBondedTransactOpts(ctx, pos.Attack().ToGIndex(), cfg.Opts)
err = g.sendMove(ctx, func() (*gethtypes.Transaction, error) {
return g.Game.Attack(transactOpts, big.NewInt(claimIdx), claim)
return g.Game.Attack(transactOpts, claimData.Claim, big.NewInt(claimIdx), claim)
})
if err != nil {
if cfg.ignoreDupes && g.hasClaim(ctx, claimIdx, attackPos, claim) {
......@@ -589,7 +589,7 @@ func (g *OutputGameHelper) Defend(ctx context.Context, claimIdx int64, claim com
transactOpts := g.makeBondedTransactOpts(ctx, defendPos.ToGIndex(), cfg.Opts)
err = g.sendMove(ctx, func() (*gethtypes.Transaction, error) {
return g.Game.Defend(transactOpts, big.NewInt(claimIdx), claim)
return g.Game.Defend(transactOpts, claimData.Claim, big.NewInt(claimIdx), claim)
})
if err != nil {
if cfg.ignoreDupes && g.hasClaim(ctx, claimIdx, defendPos, claim) {
......
......@@ -132,8 +132,8 @@
"sourceCodeHash": "0x918c395ac5d77357f2551616aad0613e68893862edd14e554623eb16ee6ba148"
},
"src/dispute/FaultDisputeGame.sol": {
"initCodeHash": "0x923d5c668370010e84a0318a813c2979962b1007d692344f5496bcda6fa85e53",
"sourceCodeHash": "0xf77ee4322aeb08559469c3bd42ec266547a170b48ea1cffcf633de80543f3c09"
"initCodeHash": "0x5ea5b544b8d7b32f55f7864c25a2443a5db363ffd1c66e0799cbc7bccaf98526",
"sourceCodeHash": "0xe8d90f1a8f92732707e370767df260bc806be5be9808f150ebed2c6caa158734"
},
"src/dispute/weth/DelayedWETH.sol": {
"initCodeHash": "0xb9bbe005874922cd8f499e7a0a092967cfca03e012c1e41912b0c77481c71777",
......
......@@ -106,6 +106,11 @@
},
{
"inputs": [
{
"internalType": "Claim",
"name": "_disputed",
"type": "bytes32"
},
{
"internalType": "uint256",
"name": "_parentIndex",
......@@ -303,6 +308,11 @@
},
{
"inputs": [
{
"internalType": "Claim",
"name": "_disputed",
"type": "bytes32"
},
{
"internalType": "uint256",
"name": "_parentIndex",
......@@ -538,6 +548,11 @@
},
{
"inputs": [
{
"internalType": "Claim",
"name": "_disputed",
"type": "bytes32"
},
{
"internalType": "uint256",
"name": "_challengeIndex",
......@@ -958,6 +973,11 @@
"name": "InvalidDataRemainder",
"type": "error"
},
{
"inputs": [],
"name": "InvalidDisputedClaimIndex",
"type": "error"
},
{
"inputs": [],
"name": "InvalidHeader",
......
......@@ -116,6 +116,11 @@
},
{
"inputs": [
{
"internalType": "Claim",
"name": "_disputed",
"type": "bytes32"
},
{
"internalType": "uint256",
"name": "_parentIndex",
......@@ -326,6 +331,11 @@
},
{
"inputs": [
{
"internalType": "Claim",
"name": "_disputed",
"type": "bytes32"
},
{
"internalType": "uint256",
"name": "_parentIndex",
......@@ -561,6 +571,11 @@
},
{
"inputs": [
{
"internalType": "Claim",
"name": "_disputed",
"type": "bytes32"
},
{
"internalType": "uint256",
"name": "_challengeIndex",
......@@ -999,6 +1014,11 @@
"name": "InvalidDataRemainder",
"type": "error"
},
{
"inputs": [],
"name": "InvalidDisputedClaimIndex",
"type": "error"
},
{
"inputs": [],
"name": "InvalidHeader",
......
......@@ -69,8 +69,8 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone, ISemver {
uint256 internal constant HEADER_BLOCK_NUMBER_INDEX = 8;
/// @notice Semantic version.
/// @custom:semver 1.1.1
string public constant version = "1.1.1";
/// @custom:semver 1.2.0
string public constant version = "1.2.0";
/// @notice The starting timestamp of the game
Timestamp public createdAt;
......@@ -312,16 +312,20 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone, ISemver {
}
/// @notice Generic move function, used for both `attack` and `defend` moves.
/// @param _disputed The disputed `Claim`.
/// @param _challengeIndex The index of the claim being moved against.
/// @param _claim The claim at the next logical position in the game.
/// @param _isAttack Whether or not the move is an attack or defense.
function move(uint256 _challengeIndex, Claim _claim, bool _isAttack) public payable virtual {
function move(Claim _disputed, uint256 _challengeIndex, Claim _claim, bool _isAttack) public payable virtual {
// INVARIANT: Moves cannot be made unless the game is currently in progress.
if (status != GameStatus.IN_PROGRESS) revert GameNotInProgress();
// Get the parent. If it does not exist, the call will revert with OOB.
ClaimData memory parent = claimData[_challengeIndex];
// INVARIANT: The claim at the _challengeIndex must be the disputed claim.
if (Claim.unwrap(parent.claim) != Claim.unwrap(_disputed)) revert InvalidDisputedClaimIndex();
// Compute the position that the claim commits to. Because the parent's position is already
// known, we can compute the next position by moving left or right depending on whether
// or not the move is an attack or defense.
......@@ -412,13 +416,13 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone, ISemver {
}
/// @inheritdoc IFaultDisputeGame
function attack(uint256 _parentIndex, Claim _claim) external payable {
move(_parentIndex, _claim, true);
function attack(Claim _disputed, uint256 _parentIndex, Claim _claim) external payable {
move(_disputed, _parentIndex, _claim, true);
}
/// @inheritdoc IFaultDisputeGame
function defend(uint256 _parentIndex, Claim _claim) external payable {
move(_parentIndex, _claim, false);
function defend(Claim _disputed, uint256 _parentIndex, Claim _claim) external payable {
move(_disputed, _parentIndex, _claim, false);
}
/// @inheritdoc IFaultDisputeGame
......
......@@ -88,11 +88,22 @@ contract PermissionedDisputeGame is FaultDisputeGame {
}
/// @notice Generic move function, used for both `attack` and `defend` moves.
/// @param _challengeIndex The index of the claim being moved against.
/// @notice _disputed The disputed `Claim`.
/// @param _challengeIndex The index of the claim being moved against. This must match the `_disputed` claim.
/// @param _claim The claim at the next logical position in the game.
/// @param _isAttack Whether or not the move is an attack or defense.
function move(uint256 _challengeIndex, Claim _claim, bool _isAttack) public payable override onlyAuthorized {
super.move(_challengeIndex, _claim, _isAttack);
function move(
Claim _disputed,
uint256 _challengeIndex,
Claim _claim,
bool _isAttack
)
public
payable
override
onlyAuthorized
{
super.move(_disputed, _challengeIndex, _claim, _isAttack);
}
/// @inheritdoc IInitializable
......
......@@ -34,14 +34,18 @@ interface IFaultDisputeGame is IDisputeGame {
event Move(uint256 indexed parentIndex, Claim indexed claim, address indexed claimant);
/// @notice Attack a disagreed upon `Claim`.
/// @param _parentIndex Index of the `Claim` to attack in the `claimData` array.
/// @param _disputed The `Claim` being attacked.
/// @param _parentIndex Index of the `Claim` to attack in the `claimData` array. This must match the `_disputed`
/// claim.
/// @param _claim The `Claim` at the relative attack position.
function attack(uint256 _parentIndex, Claim _claim) external payable;
function attack(Claim _disputed, uint256 _parentIndex, Claim _claim) external payable;
/// @notice Defend an agreed upon `Claim`.
/// @param _parentIndex Index of the claim to defend in the `claimData` array.
/// @notice _disputed The `Claim` being defended.
/// @param _parentIndex Index of the claim to defend in the `claimData` array. This must match the `_disputed`
/// claim.
/// @param _claim The `Claim` at the relative defense position.
function defend(uint256 _parentIndex, Claim _claim) external payable;
function defend(Claim _disputed, uint256 _parentIndex, Claim _claim) external payable;
/// @notice Perform an instruction step via an on-chain fault proof processor.
/// @dev This function should point to a fault proof processor in order to execute
......
......@@ -45,8 +45,8 @@ error CannotDefendRootClaim();
/// @notice Thrown when a claim is attempting to be made that already exists.
error ClaimAlreadyExists();
/// @notice Thrown when a given claim is invalid.
error InvalidClaim();
/// @notice Thrown when a disputed claim does not match its index in the game.
error InvalidDisputedClaimIndex();
/// @notice Thrown when an action that requires the game to be `IN_PROGRESS` is invoked when
/// the game is not in progress.
......
......@@ -218,11 +218,12 @@ contract HonestGameSolver is GameSolver {
bool isAttack = _direction == Direction.Attack;
uint256 bond = GAME.getRequiredBond(_movePos);
(,,,, Claim disputed,,) = GAME.claimData(_challengeIndex);
move_ = Move({
kind: isAttack ? MoveKind.Attack : MoveKind.Defend,
value: bond,
data: abi.encodeCall(FaultDisputeGame.move, (_challengeIndex, claimAt(_movePos), isAttack))
data: abi.encodeCall(FaultDisputeGame.move, (disputed, _challengeIndex, claimAt(_movePos), isAttack))
});
}
......
......@@ -131,13 +131,16 @@ contract PermissionedDisputeGame_Test is PermissionedDisputeGame_Init {
vm.startPrank(CHALLENGER, CHALLENGER);
uint256 firstBond = _getRequiredBond(0);
vm.deal(CHALLENGER, firstBond);
gameProxy.attack{ value: firstBond }(0, Claim.wrap(0));
(,,,, Claim disputed,,) = gameProxy.claimData(0);
gameProxy.attack{ value: firstBond }(disputed, 0, Claim.wrap(0));
uint256 secondBond = _getRequiredBond(1);
vm.deal(CHALLENGER, secondBond);
gameProxy.defend{ value: secondBond }(1, Claim.wrap(0));
(,,,, disputed,,) = gameProxy.claimData(1);
gameProxy.defend{ value: secondBond }(disputed, 1, Claim.wrap(0));
uint256 thirdBond = _getRequiredBond(2);
vm.deal(CHALLENGER, thirdBond);
gameProxy.move{ value: thirdBond }(2, Claim.wrap(0), true);
(,,,, disputed,,) = gameProxy.claimData(2);
gameProxy.move{ value: thirdBond }(disputed, 2, Claim.wrap(0), true);
vm.stopPrank();
}
......@@ -146,13 +149,16 @@ contract PermissionedDisputeGame_Test is PermissionedDisputeGame_Init {
vm.startPrank(PROPOSER, PROPOSER);
uint256 firstBond = _getRequiredBond(0);
vm.deal(PROPOSER, firstBond);
gameProxy.attack{ value: firstBond }(0, Claim.wrap(0));
(,,,, Claim disputed,,) = gameProxy.claimData(0);
gameProxy.attack{ value: firstBond }(disputed, 0, Claim.wrap(0));
uint256 secondBond = _getRequiredBond(1);
vm.deal(PROPOSER, secondBond);
gameProxy.defend{ value: secondBond }(1, Claim.wrap(0));
(,,,, disputed,,) = gameProxy.claimData(1);
gameProxy.defend{ value: secondBond }(disputed, 1, Claim.wrap(0));
uint256 thirdBond = _getRequiredBond(2);
vm.deal(PROPOSER, thirdBond);
gameProxy.move{ value: thirdBond }(2, Claim.wrap(0), true);
(,,,, disputed,,) = gameProxy.claimData(2);
gameProxy.move{ value: thirdBond }(disputed, 2, Claim.wrap(0), true);
vm.stopPrank();
}
......@@ -162,12 +168,13 @@ contract PermissionedDisputeGame_Test is PermissionedDisputeGame_Init {
vm.assume(_p != PROPOSER && _p != CHALLENGER);
vm.startPrank(_p, _p);
(,,,, Claim disputed,,) = gameProxy.claimData(0);
vm.expectRevert(BadAuth.selector);
gameProxy.attack(0, Claim.wrap(0));
gameProxy.attack(disputed, 0, Claim.wrap(0));
vm.expectRevert(BadAuth.selector);
gameProxy.defend(1, Claim.wrap(0));
gameProxy.defend(disputed, 0, Claim.wrap(0));
vm.expectRevert(BadAuth.selector);
gameProxy.move(2, Claim.wrap(0), true);
gameProxy.move(disputed, 0, Claim.wrap(0), true);
vm.expectRevert(BadAuth.selector);
gameProxy.step(0, true, absolutePrestateData, hex"");
vm.stopPrank();
......
......@@ -89,7 +89,8 @@ contract RandomClaimActor is StdUtils {
totalBonded += _bondAmount;
GAME.move{ value: _bondAmount }(_parentIndex, _claim, _isAttack);
(,,,, Claim disputed,,) = GAME.claimData(_parentIndex);
GAME.move{ value: _bondAmount }(disputed, _parentIndex, _claim, _isAttack);
}
fallback() external payable { }
......
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