Commit 18e9d9fd authored by Adrian Sutton's avatar Adrian Sutton

op-challenger: Translate positions for the bottom providers.

parent 1bdd60d1
...@@ -3,6 +3,7 @@ package alphabet ...@@ -3,6 +3,7 @@ package alphabet
import ( import (
"context" "context"
"errors" "errors"
"fmt"
"math/big" "math/big"
"strings" "strings"
...@@ -43,7 +44,7 @@ func (ap *AlphabetTraceProvider) GetStepData(ctx context.Context, i types.Positi ...@@ -43,7 +44,7 @@ func (ap *AlphabetTraceProvider) GetStepData(ctx context.Context, i types.Positi
traceIndex = traceIndex.Sub(traceIndex, big.NewInt(1)) traceIndex = traceIndex.Sub(traceIndex, big.NewInt(1))
// The index cannot be larger than the maximum index as computed by the depth. // The index cannot be larger than the maximum index as computed by the depth.
if traceIndex.Cmp(big.NewInt(int64(ap.maxLen))) >= 0 { if traceIndex.Cmp(big.NewInt(int64(ap.maxLen))) >= 0 {
return nil, nil, nil, ErrIndexTooLarge return nil, nil, nil, fmt.Errorf("%w traceIndex: %v max: %v pos: %v", ErrIndexTooLarge, traceIndex, ap.maxLen, i)
} }
// We extend the deepest hash to the maximum depth if the trace is not expansive. // We extend the deepest hash to the maximum depth if the trace is not expansive.
if traceIndex.Cmp(big.NewInt(int64(len(ap.state)))) >= 0 { if traceIndex.Cmp(big.NewInt(int64(len(ap.state)))) >= 0 {
...@@ -55,7 +56,7 @@ func (ap *AlphabetTraceProvider) GetStepData(ctx context.Context, i types.Positi ...@@ -55,7 +56,7 @@ func (ap *AlphabetTraceProvider) GetStepData(ctx context.Context, i types.Positi
// Get returns the claim value at the given index in the trace. // Get returns the claim value at the given index in the trace.
func (ap *AlphabetTraceProvider) Get(ctx context.Context, i types.Position) (common.Hash, error) { func (ap *AlphabetTraceProvider) Get(ctx context.Context, i types.Position) (common.Hash, error) {
if uint64(i.Depth()) > ap.depth { if uint64(i.Depth()) > ap.depth {
return common.Hash{}, ErrIndexTooLarge return common.Hash{}, fmt.Errorf("%w depth: %v max: %v", ErrIndexTooLarge, i.Depth(), ap.depth)
} }
// Step data returns the pre-state, so add 1 to get the state for index i // Step data returns the pre-state, so add 1 to get the state for index i
ti := i.TraceIndex(int(ap.depth)) ti := i.TraceIndex(int(ap.depth))
......
...@@ -57,8 +57,11 @@ func newSplitProviderSelector(topProvider types.TraceProvider, topDepth int, bot ...@@ -57,8 +57,11 @@ func newSplitProviderSelector(topProvider types.TraceProvider, topDepth int, bot
} }
} }
provider, err := bottomProviderCreator(ctx, pre, post) provider, err := bottomProviderCreator(ctx, pre, post)
// TODO: Translate the bottom provider if err != nil {
return provider, err return nil, err
}
// Translate such that the root of the bottom game is the level below the top game leaf
return trace.Translate(provider, uint64(topDepth)+1), nil
} }
} }
......
...@@ -49,6 +49,60 @@ func TestErrorWhenRefAboveTopGameLeafButPositionInBottom(t *testing.T) { ...@@ -49,6 +49,60 @@ func TestErrorWhenRefAboveTopGameLeafButPositionInBottom(t *testing.T) {
} }
} }
func TestTranslatePositionsForBottomProvider(t *testing.T) {
tests := []struct {
name string
setup func(t *testing.T, gameBuilder *test.GameBuilder) (ref types.Claim, pos types.Position, expectPre types.Claim, expectPost types.Claim)
}{
// There are 4 leaf nodes that can be accessed in the top tree of depth 3: 8, 10, 12, 14
// Then you can attack and defend any of those to challenge all blocks
{"attackTopLeafGIndex8", attackTopLeafGIndex8},
{"defendTopLeafGIndex8", defendTopLeafGIndex8},
{"attackTopLeafGIndex10", attackTopLeafGIndex10},
{"defendTopLeafGIndex10", defendTopLeafGIndex10},
{"attackTopLeafGIndex12", attackTopLeafGIndex12},
{"defendTopLeafGIndex12", defendTopLeafGIndex12},
{"attackTopLeafGIndex14", attackTopLeafGIndex14},
{"attackTopLeafGIndex14", defendTopLeafGIndex14},
}
for _, tCase := range tests {
tCase := tCase
t.Run(tCase.name, func(t *testing.T) {
_, selector, gameBuilder := setupAlphabetSplitSelector(t)
ref, pos, _, _ := tCase.setup(t, gameBuilder)
provider, err := selector(context.Background(), gameBuilder.Game, ref, pos)
require.NoError(t, err)
claimPos := pos
localClaimPos := types.NewPositionFromGIndex(big.NewInt(1))
requireSameValue(t, provider, claimPos, asBottomTraceProvider(t, provider).AlphabetTraceProvider, localClaimPos)
requireSameValue(t, provider, claimPos.Attack(), asBottomTraceProvider(t, provider).AlphabetTraceProvider, localClaimPos.Attack())
requireSameValue(t, provider, claimPos.Attack().Defend(), asBottomTraceProvider(t, provider).AlphabetTraceProvider, localClaimPos.Attack().Defend())
// TODO: Work out how to handle the unusual case of defending the root claim of the bottom provider which is now possible.
//requireSameValue(t, provider, claimPos.Defend(), asBottomTraceProvider(t, provider).AlphabetTraceProvider, localClaimPos.Defend())
})
}
}
func requireSameValue(t *testing.T, a types.TraceProvider, aPos types.Position, b types.TraceProvider, bPos types.Position) {
// Check Get returns the same results
aValue, err := a.Get(context.Background(), aPos)
require.NoError(t, err)
bValue, err := b.Get(context.Background(), bPos)
require.NoError(t, err)
require.Equal(t, aValue, bValue)
// Check GetStepData returns the same results
aPrestate, aProofData, aPreimageData, err := a.GetStepData(context.Background(), aPos)
require.NoError(t, err)
bPrestate, bProofData, bPreimageData, err := b.GetStepData(context.Background(), bPos)
require.NoError(t, err)
require.Equal(t, aPrestate, bPrestate)
require.Equal(t, aProofData, bProofData)
require.Equal(t, aPreimageData, bPreimageData)
}
func TestBottomProviderAttackingTopLeaf(t *testing.T) { func TestBottomProviderAttackingTopLeaf(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
...@@ -85,25 +139,24 @@ func TestBottomProviderAttackingTopLeaf(t *testing.T) { ...@@ -85,25 +139,24 @@ func TestBottomProviderAttackingTopLeaf(t *testing.T) {
// Check we get the same pre and post for any reference claim lower in the game // Check we get the same pre and post for any reference claim lower in the game
var testDescendantClaims func(ref types.Claim, pos types.Position) var testDescendantClaims func(ref types.Claim, pos types.Position)
testDescendantClaims = func(ref types.Claim, pos types.Position) { testDescendantClaims = func(ref types.Claim, pos types.Position) {
// For each reference, check it works with the same position or attack or defend positions // For each reference claim, check it works with the claim position, or attacking or defending the claim
// from there.
runTest(ref, pos) runTest(ref, pos)
runTest(ref, pos.Attack()) runTest(ref, pos.Attack())
runTest(ref, pos.Defend()) runTest(ref, pos.Defend())
if pos.Depth() >= topDepth+bottomDepth { if pos.Depth() >= topDepth+bottomDepth {
return return
} }
gameBuilder.SeqFrom(ref).AttackCorrect()
attackRef := latestClaim(gameBuilder)
gameBuilder.SeqFrom(ref).DefendCorrect()
defendRef := latestClaim(gameBuilder)
// If the ref is the leaf of the top claim, ensure we respect whether the test is setup // If the ref is the leaf of the top claim, ensure we respect whether the test is setup
// to attack or defend the top leaf claim. // to attack or defend the top leaf claim.
if ref.Depth() != topDepth || pos.ToGIndex().Cmp(attackRef.Position.ToGIndex()) == 0 { if ref.Depth() != topDepth || !pos.RightOf(ref.Position) {
gameBuilder.SeqFrom(ref).AttackCorrect()
attackRef := latestClaim(gameBuilder)
testDescendantClaims(attackRef, attackRef.Position) testDescendantClaims(attackRef, attackRef.Position)
} }
if ref.Depth() != topDepth || pos.ToGIndex().Cmp(defendRef.Position.ToGIndex()) == 0 { if ref.Depth() != topDepth || pos.RightOf(ref.Position) {
gameBuilder.SeqFrom(ref).DefendCorrect()
defendRef := latestClaim(gameBuilder)
testDescendantClaims(defendRef, defendRef.Position) testDescendantClaims(defendRef, defendRef.Position)
} }
} }
...@@ -239,12 +292,19 @@ func requireBottomProviderForClaims(t *testing.T, actual types.TraceProvider, ex ...@@ -239,12 +292,19 @@ func requireBottomProviderForClaims(t *testing.T, actual types.TraceProvider, ex
"should expect adjacent top level trace indices") "should expect adjacent top level trace indices")
} }
bottomProvider, ok := actual.(*bottomTraceProvider) bottomProvider := asBottomTraceProvider(t, actual)
require.True(t, ok)
require.Equal(t, expectedPre, bottomProvider.pre, "Incorrect pre claim") require.Equal(t, expectedPre, bottomProvider.pre, "Incorrect pre claim")
require.Equal(t, expectedPost, bottomProvider.post, "Incorrect post claim") require.Equal(t, expectedPost, bottomProvider.post, "Incorrect post claim")
} }
func asBottomTraceProvider(t *testing.T, actual types.TraceProvider) *bottomTraceProvider {
translatingProvider, ok := actual.(*trace.TranslatingProvider)
require.True(t, ok)
bottomProvider, ok := translatingProvider.Original().(*bottomTraceProvider)
require.True(t, ok)
return bottomProvider
}
func setupAlphabetSplitSelector(t *testing.T) (*alphabet.AlphabetTraceProvider, trace.ProviderSelector, *test.GameBuilder) { func setupAlphabetSplitSelector(t *testing.T) (*alphabet.AlphabetTraceProvider, trace.ProviderSelector, *test.GameBuilder) {
top := alphabet.NewTraceProvider("abcdef", topDepth) top := alphabet.NewTraceProvider("abcdef", topDepth)
bottomCreator := func(ctx context.Context, pre types.Claim, post types.Claim) (types.TraceProvider, error) { bottomCreator := func(ctx context.Context, pre types.Claim, post types.Claim) (types.TraceProvider, error) {
......
...@@ -7,36 +7,43 @@ import ( ...@@ -7,36 +7,43 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
) )
type translatingProvider struct { type TranslatingProvider struct {
parentDepth uint64 rootDepth uint64
provider types.TraceProvider provider types.TraceProvider
} }
func Translate(provider types.TraceProvider, parentDepth uint64) types.TraceProvider { // Translate returns a new TraceProvider that translates any requested positions before passing them on to the
return &translatingProvider{ // specified provider.
parentDepth: parentDepth, // The translation is done such that the root node for provider is at rootDepth.
provider: provider, func Translate(provider types.TraceProvider, rootDepth uint64) types.TraceProvider {
return &TranslatingProvider{
rootDepth: rootDepth,
provider: provider,
} }
} }
func (p translatingProvider) Get(ctx context.Context, pos types.Position) (common.Hash, error) { func (p *TranslatingProvider) Original() types.TraceProvider {
relativePos, err := pos.RelativeToAncestorAtDepth(p.parentDepth) return p.provider
}
func (p *TranslatingProvider) Get(ctx context.Context, pos types.Position) (common.Hash, error) {
relativePos, err := pos.RelativeToAncestorAtDepth(p.rootDepth)
if err != nil { if err != nil {
return common.Hash{}, err return common.Hash{}, err
} }
return p.provider.Get(ctx, relativePos) return p.provider.Get(ctx, relativePos)
} }
func (p translatingProvider) GetStepData(ctx context.Context, pos types.Position) (prestate []byte, proofData []byte, preimageData *types.PreimageOracleData, err error) { func (p *TranslatingProvider) GetStepData(ctx context.Context, pos types.Position) (prestate []byte, proofData []byte, preimageData *types.PreimageOracleData, err error) {
relativePos, err := pos.RelativeToAncestorAtDepth(p.parentDepth) relativePos, err := pos.RelativeToAncestorAtDepth(p.rootDepth)
if err != nil { if err != nil {
return nil, nil, nil, err return nil, nil, nil, err
} }
return p.provider.GetStepData(ctx, relativePos) return p.provider.GetStepData(ctx, relativePos)
} }
func (p translatingProvider) AbsolutePreStateCommitment(ctx context.Context) (hash common.Hash, err error) { func (p *TranslatingProvider) AbsolutePreStateCommitment(ctx context.Context) (hash common.Hash, err error) {
return p.provider.AbsolutePreStateCommitment(ctx) return p.provider.AbsolutePreStateCommitment(ctx)
} }
var _ types.TraceProvider = (*translatingProvider)(nil) var _ types.TraceProvider = (*TranslatingProvider)(nil)
...@@ -9,7 +9,7 @@ import ( ...@@ -9,7 +9,7 @@ import (
) )
var ( var (
ErrPositionDepthTooSmall = errors.New("Position depth is too small") ErrPositionDepthTooSmall = errors.New("position depth is too small")
) )
// Position is a golang wrapper around the dispute game Position type. // Position is a golang wrapper around the dispute game Position type.
...@@ -32,6 +32,10 @@ func NewPositionFromGIndex(x *big.Int) Position { ...@@ -32,6 +32,10 @@ func NewPositionFromGIndex(x *big.Int) Position {
return NewPosition(depth, indexAtDepth) return NewPosition(depth, indexAtDepth)
} }
func (p Position) String() string {
return fmt.Sprintf("Position(depth: %v, indexAtDepth: %v)", p.depth, p.indexAtDepth)
}
func (p Position) MoveRight() Position { func (p Position) MoveRight() Position {
return Position{ return Position{
depth: p.depth, depth: p.depth,
......
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