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