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 (
"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]
// to print out fault game traces for the "abcdexyz" counter-state.
func SolverExampleOne() {
fmt.Println()
fmt.Println("Solver: Example 1")
fmt.Println()
// Construct the fault position.
canonical := "abcdefgh"
disputed := "abcdexyz"
maxDepth := 3
parent := fault.Claim{
Value: common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000768"),
Position: fault.NewPosition(0, 0),
// Root claim is z at trace index 7 from the disputed provider
root := fault.Claim{
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))
disputedProvider := fault.NewAlphabetProvider(disputed, uint64(maxDepth))
// 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.
fmt.Println("Canonical state: ", canonical)
fmt.Println("Disputed state: ", disputed)
fmt.Println("Disputed state: ", disputed)
fmt.Println()
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()
PrettyPrintAlphabetClaim("Root claim", root)
PrettyPrintAlphabetClaim("Counter claim", counter)
// Get the claim from the disputed provider.
claim, err := disputedProvider.Get(3)
claim1, err := disputedSolver.NextMove(counter)
if err != nil {
fmt.Printf("error getting claim from disputed provider: %v", err)
}
firstDisputedClaim := fault.Claim{
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()
PrettyPrintAlphabetClaim("Disputed moved", *claim1)
// Get the next claim from the disputed provider.
claim, err = disputedProvider.Get(5)
claim2, err := cannonicalSolver.NextMove(*claim1)
if err != nil {
fmt.Printf("error getting claim from disputed provider: %v", err)
}
firstDisputedClaim = fault.Claim{
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()
PrettyPrintAlphabetClaim("Cannonical move", *claim2)
}
......@@ -26,6 +26,10 @@ func (p *Position) IndexAtDepth() int {
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.
// It is equivalent to going right until the final depth has been reached.
func (p *Position) TraceIndex(maxDepth int) uint64 {
......
......@@ -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.
func (s *Solver) NextMove(claim Claim, parent Claim) (*Response, error) {
parentCorrect, err := s.agreeWithClaim(parent)
func (s *Solver) NextMove(claim Claim) (*Claim, error) {
parentCorrect, err := s.agreeWithClaim(claim.Parent)
if err != nil {
return nil, err
}
claimCorrect, err := s.agreeWithClaim(claim)
claimCorrect, err := s.agreeWithClaim(claim.ClaimData)
if err != nil {
return nil, err
}
......@@ -41,7 +41,7 @@ func (s *Solver) NextMove(claim Claim, parent Claim) (*Response, error) {
return s.attack(claim)
} else if !parentCorrect && claimCorrect {
// Do nothing, we disagree with the parent, but this claim has correctly countered it
return s.doNothing()
return nil, nil
} else if !parentCorrect && !claimCorrect {
// 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
......@@ -52,30 +52,34 @@ func (s *Solver) NextMove(claim Claim, parent Claim) (*Response, error) {
return nil, errors.New("no next move")
}
func (s *Solver) doNothing() (*Response, error) {
return nil, nil
}
// attack returns a response that attacks the claim.
func (s *Solver) attack(claim Claim) (*Response, error) {
value, err := s.traceAtPosition(claim.Attack())
func (s *Solver) attack(claim Claim) (*Claim, error) {
position := claim.Attack()
value, err := s.traceAtPosition(position)
if err != nil {
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.
func (s *Solver) defend(claim Claim) (*Response, error) {
value, err := s.traceAtPosition(claim.Defend())
func (s *Solver) defend(claim Claim) (*Claim, error) {
position := claim.Defend()
value, err := s.traceAtPosition(position)
if err != nil {
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].
func (s *Solver) agreeWithClaim(claim Claim) (bool, error) {
// agreeWithClaim returns true if the claim is correct according to the internal [TraceProvider].
func (s *Solver) agreeWithClaim(claim ClaimData) (bool, error) {
ourValue, err := s.traceAtPosition(claim.Position)
return ourValue == claim.Value, err
}
......
......@@ -20,44 +20,47 @@ func TestSolver_NextMove_Opponent(t *testing.T) {
indices := []struct {
traceIndex int
claim Claim
parent Claim
response *Response
response ClaimData
}{
{
3,
Claim{
Value: common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000364"),
Position: NewPosition(1, 0),
ClaimData: ClaimData{
Value: common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000364"),
Position: NewPosition(1, 0),
},
Parent: ClaimData{
Value: common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000768"),
Position: NewPosition(0, 0),
},
},
Claim{
Value: common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000768"),
Position: NewPosition(0, 0),
},
&Response{
Attack: false,
Value: common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000566"),
ClaimData{
Value: common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000566"),
Position: NewPosition(2, 2),
},
},
{
5,
Claim{
Value: common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000578"),
Position: NewPosition(2, 2),
},
Claim{
Value: common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000768"),
Position: NewPosition(1, 1),
ClaimData: ClaimData{
Value: common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000578"),
Position: NewPosition(2, 2),
},
Parent: ClaimData{
Value: common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000768"),
Position: NewPosition(1, 1),
},
},
&Response{
Attack: true,
Value: common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000465"),
ClaimData{
Value: common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000465"),
Position: NewPosition(3, 4),
},
},
}
for _, test := range indices {
res, err := solver.NextMove(test.claim, test.parent)
res, err := solver.NextMove(test.claim)
require.NoError(t, err)
require.Equal(t, test.response, res)
require.Equal(t, test.response, res.ClaimData)
}
}
......@@ -19,19 +19,34 @@ type TraceProvider interface {
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
Position
}
type Response struct {
Attack bool // note: can we flip this to true == going right / defending??
Value common.Hash
Parent Claim
// Claim extends ClaimData with information about the relationship between two claims.
// It uses ClaimData to break cyclicity without using pointers.
// If the position of the game is Depth 0, IndexAtDepth 0 it is the root 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.
// For full op-challenger this means executing the transaction on chain.
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