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
import (
"context"
"errors"
"fmt"
"math/big"
"strings"
......@@ -43,7 +44,7 @@ func (ap *AlphabetTraceProvider) GetStepData(ctx context.Context, i types.Positi
traceIndex = traceIndex.Sub(traceIndex, big.NewInt(1))
// The index cannot be larger than the maximum index as computed by the depth.
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.
if traceIndex.Cmp(big.NewInt(int64(len(ap.state)))) >= 0 {
......@@ -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.
func (ap *AlphabetTraceProvider) Get(ctx context.Context, i types.Position) (common.Hash, error) {
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
ti := i.TraceIndex(int(ap.depth))
......
......@@ -57,8 +57,11 @@ func newSplitProviderSelector(topProvider types.TraceProvider, topDepth int, bot
}
}
provider, err := bottomProviderCreator(ctx, pre, post)
// TODO: Translate the bottom provider
return provider, err
if err != nil {
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) {
}
}
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) {
tests := []struct {
name string
......@@ -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
var 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
// from there.
// For each reference claim, check it works with the claim position, or attacking or defending the claim
runTest(ref, pos)
runTest(ref, pos.Attack())
runTest(ref, pos.Defend())
if pos.Depth() >= topDepth+bottomDepth {
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
// 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)
}
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)
}
}
......@@ -239,12 +292,19 @@ func requireBottomProviderForClaims(t *testing.T, actual types.TraceProvider, ex
"should expect adjacent top level trace indices")
}
bottomProvider, ok := actual.(*bottomTraceProvider)
require.True(t, ok)
bottomProvider := asBottomTraceProvider(t, actual)
require.Equal(t, expectedPre, bottomProvider.pre, "Incorrect pre 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) {
top := alphabet.NewTraceProvider("abcdef", topDepth)
bottomCreator := func(ctx context.Context, pre types.Claim, post types.Claim) (types.TraceProvider, error) {
......
......@@ -7,36 +7,43 @@ import (
"github.com/ethereum/go-ethereum/common"
)
type translatingProvider struct {
parentDepth uint64
provider types.TraceProvider
type TranslatingProvider struct {
rootDepth uint64
provider types.TraceProvider
}
func Translate(provider types.TraceProvider, parentDepth uint64) types.TraceProvider {
return &translatingProvider{
parentDepth: parentDepth,
provider: provider,
// Translate returns a new TraceProvider that translates any requested positions before passing them on to the
// specified provider.
// The translation is done such that the root node for provider is at rootDepth.
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) {
relativePos, err := pos.RelativeToAncestorAtDepth(p.parentDepth)
func (p *TranslatingProvider) Original() types.TraceProvider {
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 {
return common.Hash{}, err
}
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) {
relativePos, err := pos.RelativeToAncestorAtDepth(p.parentDepth)
func (p *TranslatingProvider) GetStepData(ctx context.Context, pos types.Position) (prestate []byte, proofData []byte, preimageData *types.PreimageOracleData, err error) {
relativePos, err := pos.RelativeToAncestorAtDepth(p.rootDepth)
if err != nil {
return nil, nil, nil, err
}
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)
}
var _ types.TraceProvider = (*translatingProvider)(nil)
var _ types.TraceProvider = (*TranslatingProvider)(nil)
......@@ -9,7 +9,7 @@ import (
)
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.
......@@ -32,6 +32,10 @@ func NewPositionFromGIndex(x *big.Int) Position {
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 {
return Position{
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