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 {
// IsDuplicate returns true if the provided [Claim] already exists in the game state.
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 {
......@@ -38,11 +46,12 @@ type extendedClaim struct {
type gameState struct {
root ClaimData
claims map[ClaimData]*extendedClaim
depth uint64
}
// NewGameState returns a new game state.
// 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[root.ClaimData] = &extendedClaim{
self: root,
......@@ -52,6 +61,7 @@ func NewGameState(root Claim) *gameState {
return &gameState{
root: root.ClaimData,
claims: claims,
depth: depth,
}
}
......@@ -115,3 +125,46 @@ func (g *gameState) getParent(claim Claim) (Claim, error) {
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 (
"github.com/stretchr/testify/require"
)
func createTestClaims() (Claim, Claim, Claim) {
top := Claim{
const testMaxDepth = 3
func createTestClaims() (Claim, Claim, Claim, Claim) {
// root & middle are from the trace "abcdexyz"
// top & bottom are from the trace "abcdefgh"
root := Claim{
ClaimData: ClaimData{
Value: common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000768"),
Value: common.HexToHash("0x000000000000000000000000000000000000000000000000000000000000077a"),
Position: NewPosition(0, 0),
},
Parent: ClaimData{},
// Root claim has no parent
}
middle := Claim{
top := Claim{
ClaimData: ClaimData{
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,
}
......@@ -27,71 +37,83 @@ func createTestClaims() (Claim, Claim, Claim) {
bottom := Claim{
ClaimData: ClaimData{
Value: common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000465"),
Position: NewPosition(2, 2),
Position: NewPosition(3, 4),
},
Parent: middle.ClaimData,
}
return top, middle, bottom
return root, top, middle, bottom
}
func TestIsDuplicate(t *testing.T) {
// Setup the game state.
top, middle, bottom := createTestClaims()
g := NewGameState(top)
err := g.Put(middle)
require.NoError(t, err)
root, top, middle, bottom := createTestClaims()
g := NewGameState(root, testMaxDepth)
require.NoError(t, g.Put(top))
// 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(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))
}
// 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]
// instance errors when the root claim already exists in state.
func TestGame_PutAll_RootAlreadyExists(t *testing.T) {
// Setup the game state.
top, _, _ := createTestClaims()
g := NewGameState(top)
root, _, _, _ := createTestClaims()
g := NewGameState(root, testMaxDepth)
// 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)
}
// TestGame_PutAll_AlreadyExists tests the [Game.PutAll] method using a [gameState]
// instance errors when the given claim already exists in state.
func TestGame_PutAll_AlreadyExists(t *testing.T) {
// Setup the game state.
top, middle, _ := createTestClaims()
g := NewGameState(top)
root, top, middle, bottom := createTestClaims()
g := NewGameState(root, testMaxDepth)
// Put the next claim into state.
err := g.PutAll([]Claim{middle})
err := g.PutAll([]Claim{top, middle})
require.NoError(t, err)
// Try to put the root claim into the game state again.
err = g.PutAll([]Claim{middle})
err = g.PutAll([]Claim{middle, bottom})
require.ErrorIs(t, err, ErrClaimExists)
}
// TestGame_PutAll_ParentsAndChildren tests the [Game.PutAll] method using a [gameState] instance.
func TestGame_PutAll_ParentsAndChildren(t *testing.T) {
// Setup the game state.
top, middle, bottom := createTestClaims()
g := NewGameState(top)
root, top, middle, bottom := createTestClaims()
g := NewGameState(root, testMaxDepth)
// 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.Equal(t, parent, Claim{})
// 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.Equal(t, parent, root)
parent, err = g.getParent(middle)
require.NoError(t, err)
require.Equal(t, parent, top)
......@@ -100,24 +122,12 @@ func TestGame_PutAll_ParentsAndChildren(t *testing.T) {
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]
// instance errors when the given claim already exists in state.
func TestGame_Put_AlreadyExists(t *testing.T) {
// Setup the game state.
top, middle, _ := createTestClaims()
g := NewGameState(top)
top, middle, _, _ := createTestClaims()
g := NewGameState(top, testMaxDepth)
// Put the next claim into state.
err := g.Put(middle)
......@@ -131,24 +141,29 @@ func TestGame_Put_AlreadyExists(t *testing.T) {
// TestGame_Put_ParentsAndChildren tests the [Game.Put] method using a [gameState] instance.
func TestGame_Put_ParentsAndChildren(t *testing.T) {
// Setup the game state.
top, middle, bottom := createTestClaims()
g := NewGameState(top)
root, top, middle, bottom := createTestClaims()
g := NewGameState(root, testMaxDepth)
// 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.Equal(t, parent, Claim{})
// Put the middle claim into the game state.
// We should expect no parent to exist, yet.
// Put + Check Top
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)
require.NoError(t, err)
parent, err = g.getParent(middle)
require.NoError(t, err)
require.Equal(t, parent, top)
// Put the bottom claim into the game state.
// We should expect the parent to be the claim we just added.
// Put + Check Top Bottom
err = g.Put(bottom)
require.NoError(t, err)
parent, err = g.getParent(bottom)
......@@ -159,11 +174,15 @@ func TestGame_Put_ParentsAndChildren(t *testing.T) {
// TestGame_ClaimPairs tests the [Game.ClaimPairs] method using a [gameState] instance.
func TestGame_ClaimPairs(t *testing.T) {
// Setup the game state.
top, middle, bottom := createTestClaims()
g := NewGameState(top)
root, top, middle, bottom := createTestClaims()
g := NewGameState(root, testMaxDepth)
// Add middle claim to the game state.
err := g.Put(middle)
// Add top claim to the game state.
err := g.Put(top)
require.NoError(t, err)
// Add the middle claim to the game state.
err = g.Put(middle)
require.NoError(t, err)
// Add the bottom claim to the game state.
......@@ -171,7 +190,47 @@ func TestGame_ClaimPairs(t *testing.T) {
require.NoError(t, err)
// Validate claim pairs.
expected := []Claim{top, middle, bottom}
expected := []Claim{root, top, middle, bottom}
claims := g.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 {
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 {
claimDataError bool
claimLenError bool
......
......@@ -22,7 +22,7 @@ func NewOrchestrator(maxDepth uint64, traces []TraceProvider, names []string, ro
}
log.Info("Starting game", "root_letter", string(root.Value[31:]))
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.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