Commit a6cd52cc authored by OptimismBot's avatar OptimismBot Committed by GitHub

Merge pull request #6149 from ethereum-optimism/jg/updates_types

op-challenger: Update Claim types
parents e3300c42 1f35cabc
...@@ -8,76 +8,72 @@ import ( ...@@ -8,76 +8,72 @@ import (
"github.com/ethereum-optimism/optimism/op-challenger/fault" "github.com/ethereum-optimism/optimism/op-challenger/fault"
) )
func PrettyPrintAlphabetClaim(name string, claim fault.Claim) {
value := claim.Value
idx := value[30]
letter := value[31]
if claim.IsRoot() {
fmt.Printf("%s\ttrace %v letter %c\n", name, idx, letter)
} else {
fmt.Printf("%s\ttrace %v letter %c is attack %v\n", name, idx, letter, !claim.DefendsParent())
}
}
// SolverExampleOne uses the [fault.Solver] with a [fault.AlphabetProvider] // SolverExampleOne uses the [fault.Solver] with a [fault.AlphabetProvider]
// to print out fault game traces for the "abcdexyz" counter-state. // to print out fault game traces for the "abcdexyz" counter-state.
func SolverExampleOne() { func SolverExampleOne() {
fmt.Println()
fmt.Println("Solver: Example 1") fmt.Println("Solver: Example 1")
fmt.Println()
// Construct the fault position. // Construct the fault position.
canonical := "abcdefgh" canonical := "abcdefgh"
disputed := "abcdexyz" disputed := "abcdexyz"
maxDepth := 3 maxDepth := 3
parent := fault.Claim{ // Root claim is z at trace index 7 from the disputed provider
Value: common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000768"), root := fault.Claim{
Position: fault.NewPosition(0, 0), ClaimData: fault.ClaimData{
Value: common.HexToHash("0x000000000000000000000000000000000000000000000000000000000000077a"),
Position: fault.NewPosition(0, 0),
},
}
// Note: We have to create the first counter claim seperately because next move does not know how to counter
// the root claim at this time.
// Counter claim is d at trace index 3 from the canonical provider
counter := fault.Claim{
ClaimData: fault.ClaimData{
Value: common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000364"),
Position: fault.NewPosition(1, 0),
},
Parent: root.ClaimData,
} }
canonicalProvider := fault.NewAlphabetProvider(canonical, uint64(maxDepth)) canonicalProvider := fault.NewAlphabetProvider(canonical, uint64(maxDepth))
disputedProvider := fault.NewAlphabetProvider(disputed, uint64(maxDepth)) disputedProvider := fault.NewAlphabetProvider(disputed, uint64(maxDepth))
// Create a solver with the canonical provider. // Create a solver with the canonical provider.
solver := fault.NewSolver(maxDepth, canonicalProvider) cannonicalSolver := fault.NewSolver(maxDepth, canonicalProvider)
disputedSolver := fault.NewSolver(maxDepth, disputedProvider)
// Print the initial state. // Print the initial state.
fmt.Println("Canonical state: ", canonical) fmt.Println("Canonical state: ", canonical)
fmt.Println("Disputed state: ", disputed) fmt.Println("Disputed state: ", disputed)
fmt.Println() fmt.Println()
fmt.Println("Proceeding with the following moves:") fmt.Println("Proceeding with the following moves:")
fmt.Println("go left to d, then right to f, then left to e") fmt.Println("go left to d, then right to x (cannonical is f), then left to e")
fmt.Println() fmt.Println()
PrettyPrintAlphabetClaim("Root claim", root)
PrettyPrintAlphabetClaim("Counter claim", counter)
// Get the claim from the disputed provider. claim1, err := disputedSolver.NextMove(counter)
claim, err := disputedProvider.Get(3)
if err != nil { if err != nil {
fmt.Printf("error getting claim from disputed provider: %v", err) fmt.Printf("error getting claim from disputed provider: %v", err)
} }
firstDisputedClaim := fault.Claim{ PrettyPrintAlphabetClaim("Disputed moved", *claim1)
Value: claim,
Position: fault.NewPosition(1, 0),
}
res, err := solver.NextMove(firstDisputedClaim, parent)
if err != nil {
fmt.Printf("error getting next move: %v", err)
}
fmt.Printf("Disputed claim: %s\n", claim)
fmt.Printf("Expected claim: %s\n", parent.Value)
fmt.Printf("Response: [Attack: %v, Value: %s]\n", res.Attack, res.Value)
fmt.Println()
// Get the next claim from the disputed provider. claim2, err := cannonicalSolver.NextMove(*claim1)
claim, err = disputedProvider.Get(5)
if err != nil { if err != nil {
fmt.Printf("error getting claim from disputed provider: %v", err) fmt.Printf("error getting claim from disputed provider: %v", err)
} }
firstDisputedClaim = fault.Claim{ PrettyPrintAlphabetClaim("Cannonical move", *claim2)
Value: claim,
Position: fault.NewPosition(2, 2),
}
res, err = solver.NextMove(firstDisputedClaim, parent)
if err != nil {
fmt.Printf("error getting next move: %v", err)
}
fmt.Printf("Disputed claim: %s\n", claim)
fmt.Printf("Expected claim: %s\n", parent.Value)
fmt.Printf("Response: [Attack: %v, Value: %s]\n", res.Attack, res.Value)
fmt.Println()
// This marks the end of the game!
if res.Attack {
fmt.Println("Game successfully completed!")
} else {
fmt.Println("Game failed!")
}
fmt.Println()
} }
...@@ -26,6 +26,10 @@ func (p *Position) IndexAtDepth() int { ...@@ -26,6 +26,10 @@ func (p *Position) IndexAtDepth() int {
return p.indexAtDepth return p.indexAtDepth
} }
func (p *Position) IsRootPosition() bool {
return p.depth == 0 && p.indexAtDepth == 0
}
// TraceIndex calculates the what the index of the claim value would be inside the trace. // TraceIndex calculates the what the index of the claim value would be inside the trace.
// It is equivalent to going right until the final depth has been reached. // It is equivalent to going right until the final depth has been reached.
func (p *Position) TraceIndex(maxDepth int) uint64 { func (p *Position) TraceIndex(maxDepth int) uint64 {
......
...@@ -22,12 +22,12 @@ func NewSolver(gameDepth int, traceProvider TraceProvider) *Solver { ...@@ -22,12 +22,12 @@ func NewSolver(gameDepth int, traceProvider TraceProvider) *Solver {
} }
// NextMove returns the next move to make given the current state of the game. // NextMove returns the next move to make given the current state of the game.
func (s *Solver) NextMove(claim Claim, parent Claim) (*Response, error) { func (s *Solver) NextMove(claim Claim) (*Claim, error) {
parentCorrect, err := s.agreeWithClaim(parent) parentCorrect, err := s.agreeWithClaim(claim.Parent)
if err != nil { if err != nil {
return nil, err return nil, err
} }
claimCorrect, err := s.agreeWithClaim(claim) claimCorrect, err := s.agreeWithClaim(claim.ClaimData)
if err != nil { if err != nil {
return nil, err return nil, err
} }
...@@ -41,7 +41,7 @@ func (s *Solver) NextMove(claim Claim, parent Claim) (*Response, error) { ...@@ -41,7 +41,7 @@ func (s *Solver) NextMove(claim Claim, parent Claim) (*Response, error) {
return s.attack(claim) return s.attack(claim)
} else if !parentCorrect && claimCorrect { } else if !parentCorrect && claimCorrect {
// Do nothing, we disagree with the parent, but this claim has correctly countered it // Do nothing, we disagree with the parent, but this claim has correctly countered it
return s.doNothing() return nil, nil
} else if !parentCorrect && !claimCorrect { } else if !parentCorrect && !claimCorrect {
// We disagree with the parent so want to counter it (which the claim is doing) // We disagree with the parent so want to counter it (which the claim is doing)
// but we also disagree with the claim so there must be a difference to the left of claim // but we also disagree with the claim so there must be a difference to the left of claim
...@@ -52,30 +52,34 @@ func (s *Solver) NextMove(claim Claim, parent Claim) (*Response, error) { ...@@ -52,30 +52,34 @@ func (s *Solver) NextMove(claim Claim, parent Claim) (*Response, error) {
return nil, errors.New("no next move") return nil, errors.New("no next move")
} }
func (s *Solver) doNothing() (*Response, error) {
return nil, nil
}
// attack returns a response that attacks the claim. // attack returns a response that attacks the claim.
func (s *Solver) attack(claim Claim) (*Response, error) { func (s *Solver) attack(claim Claim) (*Claim, error) {
value, err := s.traceAtPosition(claim.Attack()) position := claim.Attack()
value, err := s.traceAtPosition(position)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &Response{Attack: true, Value: value}, nil return &Claim{
ClaimData: ClaimData{Value: value, Position: position},
Parent: claim.ClaimData,
}, nil
} }
// defend returns a response that defends the claim. // defend returns a response that defends the claim.
func (s *Solver) defend(claim Claim) (*Response, error) { func (s *Solver) defend(claim Claim) (*Claim, error) {
value, err := s.traceAtPosition(claim.Defend()) position := claim.Defend()
value, err := s.traceAtPosition(position)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &Response{Attack: false, Value: value}, nil return &Claim{
ClaimData: ClaimData{Value: value, Position: position},
Parent: claim.ClaimData,
}, nil
} }
// agreeWithClaim returns true if the [Claim] is correct according to the internal [TraceProvider]. // agreeWithClaim returns true if the claim is correct according to the internal [TraceProvider].
func (s *Solver) agreeWithClaim(claim Claim) (bool, error) { func (s *Solver) agreeWithClaim(claim ClaimData) (bool, error) {
ourValue, err := s.traceAtPosition(claim.Position) ourValue, err := s.traceAtPosition(claim.Position)
return ourValue == claim.Value, err return ourValue == claim.Value, err
} }
......
...@@ -20,44 +20,47 @@ func TestSolver_NextMove_Opponent(t *testing.T) { ...@@ -20,44 +20,47 @@ func TestSolver_NextMove_Opponent(t *testing.T) {
indices := []struct { indices := []struct {
traceIndex int traceIndex int
claim Claim claim Claim
parent Claim response ClaimData
response *Response
}{ }{
{ {
3, 3,
Claim{ Claim{
Value: common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000364"), ClaimData: ClaimData{
Position: NewPosition(1, 0), Value: common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000364"),
Position: NewPosition(1, 0),
},
Parent: ClaimData{
Value: common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000768"),
Position: NewPosition(0, 0),
},
}, },
Claim{ ClaimData{
Value: common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000768"), Value: common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000566"),
Position: NewPosition(0, 0), Position: NewPosition(2, 2),
},
&Response{
Attack: false,
Value: common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000566"),
}, },
}, },
{ {
5, 5,
Claim{ Claim{
Value: common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000578"), ClaimData: ClaimData{
Position: NewPosition(2, 2), Value: common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000578"),
}, Position: NewPosition(2, 2),
Claim{ },
Value: common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000768"), Parent: ClaimData{
Position: NewPosition(1, 1), Value: common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000768"),
Position: NewPosition(1, 1),
},
}, },
&Response{ ClaimData{
Attack: true, Value: common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000465"),
Value: common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000465"), Position: NewPosition(3, 4),
}, },
}, },
} }
for _, test := range indices { for _, test := range indices {
res, err := solver.NextMove(test.claim, test.parent) res, err := solver.NextMove(test.claim)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, test.response, res) require.Equal(t, test.response, res.ClaimData)
} }
} }
...@@ -19,19 +19,34 @@ type TraceProvider interface { ...@@ -19,19 +19,34 @@ type TraceProvider interface {
Get(i uint64) (common.Hash, error) Get(i uint64) (common.Hash, error)
} }
type Claim struct { // ClaimData is the core of a claim. It must be unique inside a specific game.
type ClaimData struct {
Value common.Hash Value common.Hash
Position Position
} }
type Response struct { // Claim extends ClaimData with information about the relationship between two claims.
Attack bool // note: can we flip this to true == going right / defending?? // It uses ClaimData to break cyclicity without using pointers.
Value common.Hash // If the position of the game is Depth 0, IndexAtDepth 0 it is the root claim
Parent Claim // and the Parent field is empty & meaningless.
type Claim struct {
ClaimData
Parent ClaimData
}
// IsRoot returns true if this claim is the root claim.
func (c *Claim) IsRoot() bool {
return c.Position.IsRootPosition()
}
// DefendsParent returns true if the the claim is a defense (i.e. goes right) of the
// parent. It returns false if the claim is an attack (i.e. goes left) of the parent.
func (c *Claim) DefendsParent() bool {
return (c.IndexAtDepth() >> 1) != c.Parent.IndexAtDepth()
} }
// Responder takes a response action & executes. // Responder takes a response action & executes.
// For full op-challenger this means executing the transaction on chain. // For full op-challenger this means executing the transaction on chain.
type Responder interface { type Responder interface {
Respond(ctx context.Context, response Response) error Respond(ctx context.Context, response Claim) error
} }
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