Commit 0a6a781b authored by Adrian Sutton's avatar Adrian Sutton

op-challenger: Simplify game implementation

Stores claims in an array with the same order as the contracts would. Computes a claim ID by hashing the values that must be unique.
parent 0025fff1
...@@ -2,6 +2,10 @@ package types ...@@ -2,6 +2,10 @@ package types
import ( import (
"errors" "errors"
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
) )
var ( var (
...@@ -36,45 +40,35 @@ type Game interface { ...@@ -36,45 +40,35 @@ type Game interface {
MaxDepth() uint64 MaxDepth() uint64
} }
type claimEntry struct { type claimID common.Hash
ClaimData
ParentContractIndex int
}
type extendedClaim struct { func computeClaimID(claim Claim) claimID {
self Claim return claimID(crypto.Keccak256Hash(
children []claimEntry new(big.Int).SetUint64(claim.Position.ToGIndex()).Bytes(),
claim.Value.Bytes(),
big.NewInt(int64(claim.ParentContractIndex)).Bytes(),
))
} }
// gameState is a struct that represents the state of a dispute game. // gameState is a struct that represents the state of a dispute game.
// The game state implements the [Game] interface. // The game state implements the [Game] interface.
type gameState struct { type gameState struct {
agreeWithProposedOutput bool agreeWithProposedOutput bool
root claimEntry // claims is the list of claims in the same order as the contract
// contractIndicies maps a contract index to it's extended claim. claims []Claim
// This is used to perform O(1) parent lookups. claimIDs map[claimID]bool
contractIndicies map[int]*extendedClaim depth uint64
// claims maps a claim entry to it's extended claim.
claims map[claimEntry]*extendedClaim
depth uint64
} }
// NewGameState returns a new game state. // NewGameState returns a new game state.
// The provided [Claim] is used as the root node. // The provided [Claim] is used as the root node.
func NewGameState(agreeWithProposedOutput bool, root Claim, depth uint64) *gameState { func NewGameState(agreeWithProposedOutput bool, root Claim, depth uint64) *gameState {
claims := make(map[claimEntry]*extendedClaim) claimIDs := make(map[claimID]bool)
parents := make(map[int]*extendedClaim) claimIDs[computeClaimID(root)] = true
rootClaimEntry := makeClaimEntry(root)
claims[rootClaimEntry] = &extendedClaim{
self: root,
children: make([]claimEntry, 0),
}
parents[root.ContractIndex] = claims[rootClaimEntry]
return &gameState{ return &gameState{
agreeWithProposedOutput: agreeWithProposedOutput, agreeWithProposedOutput: agreeWithProposedOutput,
root: rootClaimEntry, claims: []Claim{root},
claims: claims, claimIDs: claimIDs,
contractIndicies: parents,
depth: depth, depth: depth,
} }
} }
...@@ -112,62 +106,40 @@ func (g *gameState) Put(claim Claim) error { ...@@ -112,62 +106,40 @@ func (g *gameState) Put(claim Claim) error {
if parent == nil { if parent == nil {
return errors.New("no parent claim") return errors.New("no parent claim")
} }
parent.children = append(parent.children, makeClaimEntry(claim))
claimWithExtension := &extendedClaim{ g.claims = append(g.claims, claim)
self: claim, g.claimIDs[computeClaimID(claim)] = true
children: make([]claimEntry, 0),
}
g.claims[makeClaimEntry(claim)] = claimWithExtension
g.contractIndicies[claim.ContractIndex] = claimWithExtension
return nil return nil
} }
func (g *gameState) IsDuplicate(claim Claim) bool { func (g *gameState) IsDuplicate(claim Claim) bool {
_, ok := g.claims[makeClaimEntry(claim)] return g.claimIDs[computeClaimID(claim)]
return ok
} }
func (g *gameState) Claims() []Claim { func (g *gameState) Claims() []Claim {
queue := []claimEntry{g.root} // Defensively copy to avoid modifications to the underlying array.
var out []Claim return append([]Claim(nil), g.claims...)
for len(queue) > 0 {
item := queue[0]
queue = queue[1:]
queue = append(queue, g.getChildren(item)...)
out = append(out, g.claims[item].self)
}
return out
} }
func (g *gameState) MaxDepth() uint64 { func (g *gameState) MaxDepth() uint64 {
return g.depth return g.depth
} }
func (g *gameState) getChildren(c claimEntry) []claimEntry {
return g.claims[c].children
}
func (g *gameState) GetParent(claim Claim) (Claim, error) { func (g *gameState) GetParent(claim Claim) (Claim, error) {
parent := g.getParent(claim) parent := g.getParent(claim)
if parent == nil { if parent == nil {
return Claim{}, ErrClaimNotFound return Claim{}, ErrClaimNotFound
} }
return parent.self, nil return *parent, nil
} }
func (g *gameState) getParent(claim Claim) *extendedClaim { func (g *gameState) getParent(claim Claim) *Claim {
if claim.IsRoot() { if claim.IsRoot() {
return nil return nil
} }
if parent, ok := g.contractIndicies[claim.ParentContractIndex]; ok { if claim.ParentContractIndex >= len(g.claims) || claim.ParentContractIndex < 0 {
return parent return nil
}
return nil
}
func makeClaimEntry(claim Claim) claimEntry {
return claimEntry{
ClaimData: claim.ClaimData,
ParentContractIndex: claim.ParentContractIndex,
} }
parent := g.claims[claim.ParentContractIndex]
return &parent
} }
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