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

Merge pull request #6207 from ethereum-optimism/jg/game_prep_for_step

op-challenger: Get Pre/Post claim for Step function
parents e4ca19e1 63fcc9c1
...@@ -25,6 +25,14 @@ type Game interface { ...@@ -25,6 +25,14 @@ type Game interface {
// IsDuplicate returns true if the provided [Claim] already exists in the game state. // IsDuplicate returns true if the provided [Claim] already exists in the game state.
IsDuplicate(claim Claim) bool IsDuplicate(claim Claim) bool
// PreStateClaim gets the claim which commits to the pre-state of this specific claim.
// This will return an error if it is called with a non-leaf claim.
PreStateClaim(claim Claim) (Claim, error)
// PostStateClaim gets the claim which commits to the pre-state of this specific claim.
// This will return an error if it is called with a non-leaf claim.
PostStateClaim(claim Claim) (Claim, error)
} }
type extendedClaim struct { type extendedClaim struct {
...@@ -38,11 +46,12 @@ type extendedClaim struct { ...@@ -38,11 +46,12 @@ type extendedClaim struct {
type gameState struct { type gameState struct {
root ClaimData root ClaimData
claims map[ClaimData]*extendedClaim claims map[ClaimData]*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(root Claim) *gameState { func NewGameState(root Claim, depth uint64) *gameState {
claims := make(map[ClaimData]*extendedClaim) claims := make(map[ClaimData]*extendedClaim)
claims[root.ClaimData] = &extendedClaim{ claims[root.ClaimData] = &extendedClaim{
self: root, self: root,
...@@ -52,6 +61,7 @@ func NewGameState(root Claim) *gameState { ...@@ -52,6 +61,7 @@ func NewGameState(root Claim) *gameState {
return &gameState{ return &gameState{
root: root.ClaimData, root: root.ClaimData,
claims: claims, claims: claims,
depth: depth,
} }
} }
...@@ -115,3 +125,46 @@ func (g *gameState) getParent(claim Claim) (Claim, error) { ...@@ -115,3 +125,46 @@ func (g *gameState) getParent(claim Claim) (Claim, error) {
return parent.self, nil return parent.self, nil
} }
} }
func (g *gameState) PreStateClaim(claim Claim) (Claim, error) {
// Do checks in PreStateClaim because these do not hold while walking the tree
if claim.Depth() != int(g.depth) {
return Claim{}, errors.New("Only leaf claims have pre or post state")
}
// If the claim is the far left most claim, the pre-state is pulled from the contracts & we can supply at contract index.
if claim.IndexAtDepth() == 0 {
return Claim{
ContractIndex: -1,
}, nil
}
return g.preStateClaim(claim)
}
// preStateClaim is the internal tree walker which does not do error handling
func (g *gameState) preStateClaim(claim Claim) (Claim, error) {
parent, _ := g.getParent(claim)
if claim.DefendsParent() {
return parent, nil
} else {
return g.preStateClaim(parent)
}
}
func (g *gameState) PostStateClaim(claim Claim) (Claim, error) {
// Do checks in PostStateClaim because these do not hold while walking the tree
if claim.Depth() != int(g.depth) {
return Claim{}, errors.New("Only leaf claims have pre or post state")
}
return g.postStateClaim(claim)
}
// postStateClaim is the internal tree walker which does not do error handling
func (g *gameState) postStateClaim(claim Claim) (Claim, error) {
parent, _ := g.getParent(claim)
if claim.DefendsParent() {
return g.postStateClaim(parent)
} else {
return parent, nil
}
}
...@@ -7,19 +7,29 @@ import ( ...@@ -7,19 +7,29 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
func createTestClaims() (Claim, Claim, Claim) { const testMaxDepth = 3
top := Claim{
func createTestClaims() (Claim, Claim, Claim, Claim) {
// root & middle are from the trace "abcdexyz"
// top & bottom are from the trace "abcdefgh"
root := Claim{
ClaimData: ClaimData{ ClaimData: ClaimData{
Value: common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000768"), Value: common.HexToHash("0x000000000000000000000000000000000000000000000000000000000000077a"),
Position: NewPosition(0, 0), Position: NewPosition(0, 0),
}, },
Parent: ClaimData{}, // Root claim has no parent
} }
top := Claim{
middle := Claim{
ClaimData: ClaimData{ ClaimData: ClaimData{
Value: common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000364"), Value: common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000364"),
Position: NewPosition(1, 1), Position: NewPosition(1, 0),
},
Parent: root.ClaimData,
}
middle := Claim{
ClaimData: ClaimData{
Value: common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000578"),
Position: NewPosition(2, 2),
}, },
Parent: top.ClaimData, Parent: top.ClaimData,
} }
...@@ -27,71 +37,83 @@ func createTestClaims() (Claim, Claim, Claim) { ...@@ -27,71 +37,83 @@ func createTestClaims() (Claim, Claim, Claim) {
bottom := Claim{ bottom := Claim{
ClaimData: ClaimData{ ClaimData: ClaimData{
Value: common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000465"), Value: common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000465"),
Position: NewPosition(2, 2), Position: NewPosition(3, 4),
}, },
Parent: middle.ClaimData, Parent: middle.ClaimData,
} }
return top, middle, bottom return root, top, middle, bottom
} }
func TestIsDuplicate(t *testing.T) { func TestIsDuplicate(t *testing.T) {
// Setup the game state. // Setup the game state.
top, middle, bottom := createTestClaims() root, top, middle, bottom := createTestClaims()
g := NewGameState(top) g := NewGameState(root, testMaxDepth)
err := g.Put(middle) require.NoError(t, g.Put(top))
require.NoError(t, err)
// Top + Middle should be duplicates // Root + Top should be duplicates
require.True(t, g.IsDuplicate(root))
require.True(t, g.IsDuplicate(top)) require.True(t, g.IsDuplicate(top))
require.True(t, g.IsDuplicate(middle))
// Bottom should not be a duplicate // Middle + Bottom should not be a duplicate
require.False(t, g.IsDuplicate(middle))
require.False(t, g.IsDuplicate(bottom)) require.False(t, g.IsDuplicate(bottom))
} }
// TestGame_Put_RootAlreadyExists tests the [Game.Put] method using a [gameState]
// instance errors when the root claim already exists in state.
func TestGame_Put_RootAlreadyExists(t *testing.T) {
// Setup the game state.
top, _, _, _ := createTestClaims()
g := NewGameState(top, testMaxDepth)
// Try to put the root claim into the game state again.
err := g.Put(top)
require.ErrorIs(t, err, ErrClaimExists)
}
// TestGame_PutAll_RootAlreadyExists tests the [Game.PutAll] method using a [gameState] // TestGame_PutAll_RootAlreadyExists tests the [Game.PutAll] method using a [gameState]
// instance errors when the root claim already exists in state. // instance errors when the root claim already exists in state.
func TestGame_PutAll_RootAlreadyExists(t *testing.T) { func TestGame_PutAll_RootAlreadyExists(t *testing.T) {
// Setup the game state. // Setup the game state.
top, _, _ := createTestClaims() root, _, _, _ := createTestClaims()
g := NewGameState(top) g := NewGameState(root, testMaxDepth)
// Try to put the root claim into the game state again. // Try to put the root claim into the game state again.
err := g.PutAll([]Claim{top}) err := g.PutAll([]Claim{root})
require.ErrorIs(t, err, ErrClaimExists) require.ErrorIs(t, err, ErrClaimExists)
} }
// TestGame_PutAll_AlreadyExists tests the [Game.PutAll] method using a [gameState] // TestGame_PutAll_AlreadyExists tests the [Game.PutAll] method using a [gameState]
// instance errors when the given claim already exists in state. // instance errors when the given claim already exists in state.
func TestGame_PutAll_AlreadyExists(t *testing.T) { func TestGame_PutAll_AlreadyExists(t *testing.T) {
// Setup the game state. root, top, middle, bottom := createTestClaims()
top, middle, _ := createTestClaims() g := NewGameState(root, testMaxDepth)
g := NewGameState(top)
// Put the next claim into state. err := g.PutAll([]Claim{top, middle})
err := g.PutAll([]Claim{middle})
require.NoError(t, err) require.NoError(t, err)
// Try to put the root claim into the game state again. err = g.PutAll([]Claim{middle, bottom})
err = g.PutAll([]Claim{middle})
require.ErrorIs(t, err, ErrClaimExists) require.ErrorIs(t, err, ErrClaimExists)
} }
// TestGame_PutAll_ParentsAndChildren tests the [Game.PutAll] method using a [gameState] instance. // TestGame_PutAll_ParentsAndChildren tests the [Game.PutAll] method using a [gameState] instance.
func TestGame_PutAll_ParentsAndChildren(t *testing.T) { func TestGame_PutAll_ParentsAndChildren(t *testing.T) {
// Setup the game state. // Setup the game state.
top, middle, bottom := createTestClaims() root, top, middle, bottom := createTestClaims()
g := NewGameState(top) g := NewGameState(root, testMaxDepth)
// We should not be able to get the parent of the root claim. // We should not be able to get the parent of the root claim.
parent, err := g.getParent(top) parent, err := g.getParent(root)
require.ErrorIs(t, err, ErrClaimNotFound) require.ErrorIs(t, err, ErrClaimNotFound)
require.Equal(t, parent, Claim{}) require.Equal(t, parent, Claim{})
// Put the rest of the claims in the state. // Put the rest of the claims in the state.
err = g.PutAll([]Claim{middle, bottom}) err = g.PutAll([]Claim{top, middle, bottom})
require.NoError(t, err)
parent, err = g.getParent(top)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, parent, root)
parent, err = g.getParent(middle) parent, err = g.getParent(middle)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, parent, top) require.Equal(t, parent, top)
...@@ -100,24 +122,12 @@ func TestGame_PutAll_ParentsAndChildren(t *testing.T) { ...@@ -100,24 +122,12 @@ func TestGame_PutAll_ParentsAndChildren(t *testing.T) {
require.Equal(t, parent, middle) require.Equal(t, parent, middle)
} }
// TestGame_Put_RootAlreadyExists tests the [Game.Put] method using a [gameState]
// instance errors when the root claim already exists in state.
func TestGame_Put_RootAlreadyExists(t *testing.T) {
// Setup the game state.
top, _, _ := createTestClaims()
g := NewGameState(top)
// Try to put the root claim into the game state again.
err := g.Put(top)
require.ErrorIs(t, err, ErrClaimExists)
}
// TestGame_Put_AlreadyExists tests the [Game.Put] method using a [gameState] // TestGame_Put_AlreadyExists tests the [Game.Put] method using a [gameState]
// instance errors when the given claim already exists in state. // instance errors when the given claim already exists in state.
func TestGame_Put_AlreadyExists(t *testing.T) { func TestGame_Put_AlreadyExists(t *testing.T) {
// Setup the game state. // Setup the game state.
top, middle, _ := createTestClaims() top, middle, _, _ := createTestClaims()
g := NewGameState(top) g := NewGameState(top, testMaxDepth)
// Put the next claim into state. // Put the next claim into state.
err := g.Put(middle) err := g.Put(middle)
...@@ -131,24 +141,29 @@ func TestGame_Put_AlreadyExists(t *testing.T) { ...@@ -131,24 +141,29 @@ func TestGame_Put_AlreadyExists(t *testing.T) {
// TestGame_Put_ParentsAndChildren tests the [Game.Put] method using a [gameState] instance. // TestGame_Put_ParentsAndChildren tests the [Game.Put] method using a [gameState] instance.
func TestGame_Put_ParentsAndChildren(t *testing.T) { func TestGame_Put_ParentsAndChildren(t *testing.T) {
// Setup the game state. // Setup the game state.
top, middle, bottom := createTestClaims() root, top, middle, bottom := createTestClaims()
g := NewGameState(top) g := NewGameState(root, testMaxDepth)
// We should not be able to get the parent of the root claim. // We should not be able to get the parent of the root claim.
parent, err := g.getParent(top) parent, err := g.getParent(root)
require.ErrorIs(t, err, ErrClaimNotFound) require.ErrorIs(t, err, ErrClaimNotFound)
require.Equal(t, parent, Claim{}) require.Equal(t, parent, Claim{})
// Put the middle claim into the game state. // Put + Check Top
// We should expect no parent to exist, yet. err = g.Put(top)
require.NoError(t, err)
parent, err = g.getParent(top)
require.NoError(t, err)
require.Equal(t, parent, root)
// Put + Check Top Middle
err = g.Put(middle) err = g.Put(middle)
require.NoError(t, err) require.NoError(t, err)
parent, err = g.getParent(middle) parent, err = g.getParent(middle)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, parent, top) require.Equal(t, parent, top)
// Put the bottom claim into the game state. // Put + Check Top Bottom
// We should expect the parent to be the claim we just added.
err = g.Put(bottom) err = g.Put(bottom)
require.NoError(t, err) require.NoError(t, err)
parent, err = g.getParent(bottom) parent, err = g.getParent(bottom)
...@@ -159,11 +174,15 @@ func TestGame_Put_ParentsAndChildren(t *testing.T) { ...@@ -159,11 +174,15 @@ func TestGame_Put_ParentsAndChildren(t *testing.T) {
// TestGame_ClaimPairs tests the [Game.ClaimPairs] method using a [gameState] instance. // TestGame_ClaimPairs tests the [Game.ClaimPairs] method using a [gameState] instance.
func TestGame_ClaimPairs(t *testing.T) { func TestGame_ClaimPairs(t *testing.T) {
// Setup the game state. // Setup the game state.
top, middle, bottom := createTestClaims() root, top, middle, bottom := createTestClaims()
g := NewGameState(top) g := NewGameState(root, testMaxDepth)
// Add middle claim to the game state. // Add top claim to the game state.
err := g.Put(middle) err := g.Put(top)
require.NoError(t, err)
// Add the middle claim to the game state.
err = g.Put(middle)
require.NoError(t, err) require.NoError(t, err)
// Add the bottom claim to the game state. // Add the bottom claim to the game state.
...@@ -171,7 +190,47 @@ func TestGame_ClaimPairs(t *testing.T) { ...@@ -171,7 +190,47 @@ func TestGame_ClaimPairs(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
// Validate claim pairs. // Validate claim pairs.
expected := []Claim{top, middle, bottom} expected := []Claim{root, top, middle, bottom}
claims := g.Claims() claims := g.Claims()
require.ElementsMatch(t, expected, claims) require.ElementsMatch(t, expected, claims)
} }
// TestPrePostStateOnlyOnLeafClaim tests that if PreStateClaim or PostStateClaim is called with an non-leaf claim
// those functions return an error.
func TestPrePostStateOnlyOnLeafClaim(t *testing.T) {
root, top, middle, bottom := createTestClaims()
g := NewGameState(root, testMaxDepth)
require.NoError(t, g.PutAll([]Claim{top, middle, bottom}))
_, err := g.PreStateClaim(middle)
require.Error(t, err)
_, err = g.PostStateClaim(middle)
require.Error(t, err)
}
func TestPreStateClaim(t *testing.T) {
root, top, middle, bottom := createTestClaims()
g := NewGameState(root, testMaxDepth)
require.NoError(t, g.Put(top))
require.NoError(t, g.Put(middle))
require.NoError(t, g.Put(bottom))
// Bottom trace index is 4. Pre trace index is then 3
pre, err := g.PreStateClaim(bottom)
require.NoError(t, err)
require.Equal(t, top, pre)
}
func TestPostStateClaim(t *testing.T) {
root, top, middle, bottom := createTestClaims()
g := NewGameState(root, testMaxDepth)
require.NoError(t, g.Put(top))
require.NoError(t, g.Put(middle))
require.NoError(t, g.Put(bottom))
// Bottom trace index is 4. Post trace index is then 5
post, err := g.PostStateClaim(bottom)
require.NoError(t, err)
require.Equal(t, middle, post)
}
...@@ -47,6 +47,14 @@ func (m *mockGameState) IsDuplicate(claim Claim) bool { ...@@ -47,6 +47,14 @@ func (m *mockGameState) IsDuplicate(claim Claim) bool {
return false return false
} }
func (m *mockGameState) PreStateClaim(claim Claim) (Claim, error) {
panic("unimplemented")
}
func (m *mockGameState) PostStateClaim(claim Claim) (Claim, error) {
panic("unimplemented")
}
type mockClaimFetcher struct { type mockClaimFetcher struct {
claimDataError bool claimDataError bool
claimLenError bool claimLenError bool
......
...@@ -22,7 +22,7 @@ func NewOrchestrator(maxDepth uint64, traces []TraceProvider, names []string, ro ...@@ -22,7 +22,7 @@ func NewOrchestrator(maxDepth uint64, traces []TraceProvider, names []string, ro
} }
log.Info("Starting game", "root_letter", string(root.Value[31:])) log.Info("Starting game", "root_letter", string(root.Value[31:]))
for i, trace := range traces { for i, trace := range traces {
game := NewGameState(root) game := NewGameState(root, maxDepth)
o.agents[i] = NewAgent(game, int(maxDepth), trace, &o, log.New("role", names[i])) o.agents[i] = NewAgent(game, int(maxDepth), trace, &o, log.New("role", names[i]))
o.outputChs[i] = make(chan Claim) o.outputChs[i] = make(chan Claim)
} }
......
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