Commit 33c9b5a4 authored by refcell's avatar refcell

Fix the split trace provider

parent 1e956691
...@@ -2,7 +2,6 @@ package split ...@@ -2,7 +2,6 @@ package split
import ( import (
"context" "context"
"fmt"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/types" "github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
...@@ -10,74 +9,64 @@ import ( ...@@ -10,74 +9,64 @@ import (
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
) )
var (
GetStepDataErr = fmt.Errorf("GetStepData not supported")
NoProvidersErr = fmt.Errorf("no trace providers configured")
)
var _ types.TraceProvider = (*SplitTraceProvider)(nil) var _ types.TraceProvider = (*SplitTraceProvider)(nil)
// SplitTraceProvider is a [types.TraceProvider] implementation that // SplitTraceProvider is a [types.TraceProvider] implementation that
// routes requests to the correct internal trace provider based on the // routes requests to the correct internal trace provider based on the
// depth of the requested trace. // depth of the requested trace.
type SplitTraceProvider struct { type SplitTraceProvider struct {
logger log.Logger logger log.Logger
providers []types.TraceProvider topProvider types.TraceProvider
depthTiers []uint64 bottomProvider types.TraceProvider
topDepth uint64
} }
func NewTraceProvider(logger log.Logger, providers []types.TraceProvider, depthTiers []uint64) *SplitTraceProvider { // NewTraceProvider creates a new [SplitTraceProvider] instance.
// The [topDepth] parameter specifies the depth at which the internal
// [types.TraceProvider] should be switched.
func NewTraceProvider(logger log.Logger, topProvider types.TraceProvider, bottomProvider types.TraceProvider, topDepth uint64) *SplitTraceProvider {
return &SplitTraceProvider{ return &SplitTraceProvider{
logger: logger, logger: logger,
providers: providers, topProvider: topProvider,
depthTiers: depthTiers, bottomProvider: bottomProvider,
topDepth: topDepth,
} }
} }
func (s *SplitTraceProvider) providerForDepth(depth uint64) (uint64, types.TraceProvider) { func (s *SplitTraceProvider) providerForDepth(depth uint64) (uint64, types.TraceProvider) {
reduced := uint64(0) if depth <= s.topDepth {
for i, tier := range s.depthTiers { return 0, s.topProvider
if depth <= tier {
return reduced, s.providers[i]
}
if i < len(s.providers)-1 {
reduced += tier
}
} }
return reduced, s.providers[len(s.providers)-1] return s.topDepth, s.bottomProvider
} }
// Get routes the Get request to the internal [types.TraceProvider] that // Get routes the Get request to the internal [types.TraceProvider] that
// that serves the trace index at the depth. // that serves the trace index at the depth.
func (s *SplitTraceProvider) Get(ctx context.Context, pos types.Position) (common.Hash, error) { func (s *SplitTraceProvider) Get(ctx context.Context, pos types.Position) (common.Hash, error) {
if len(s.providers) == 0 { ancestorDepth, provider := s.providerForDepth(uint64(pos.Depth()))
return common.Hash{}, NoProvidersErr relativePosition, err := pos.RelativeToAncestorAtDepth(ancestorDepth)
if err != nil {
return common.Hash{}, err
} }
reduced, provider := s.providerForDepth(uint64(pos.Depth())) return provider.Get(ctx, relativePosition)
localizedPosition := pos.Localize(reduced)
return provider.Get(ctx, localizedPosition)
} }
// AbsolutePreStateCommitment returns the absolute prestate from the lowest internal [types.TraceProvider] // AbsolutePreStateCommitment returns the absolute prestate from the lowest internal [types.TraceProvider]
func (s *SplitTraceProvider) AbsolutePreStateCommitment(ctx context.Context) (hash common.Hash, err error) { func (s *SplitTraceProvider) AbsolutePreStateCommitment(ctx context.Context) (hash common.Hash, err error) {
if len(s.providers) == 0 { return s.bottomProvider.AbsolutePreStateCommitment(ctx)
return common.Hash{}, NoProvidersErr
}
return s.providers[len(s.providers)-1].AbsolutePreStateCommitment(ctx)
} }
// AbsolutePreState routes the AbsolutePreState request to the lowest internal [types.TraceProvider]. // AbsolutePreState routes the AbsolutePreState request to the lowest internal [types.TraceProvider].
func (s *SplitTraceProvider) AbsolutePreState(ctx context.Context) (preimage []byte, err error) { func (s *SplitTraceProvider) AbsolutePreState(ctx context.Context) (preimage []byte, err error) {
if len(s.providers) == 0 { return s.bottomProvider.AbsolutePreState(ctx)
return nil, NoProvidersErr
}
return s.providers[len(s.providers)-1].AbsolutePreState(ctx)
} }
// GetStepData routes the GetStepData request to the lowest internal [types.TraceProvider]. // GetStepData routes the GetStepData request to the lowest internal [types.TraceProvider].
func (s *SplitTraceProvider) GetStepData(ctx context.Context, i types.Position) (prestate []byte, proofData []byte, preimageData *types.PreimageOracleData, err error) { func (s *SplitTraceProvider) GetStepData(ctx context.Context, pos types.Position) (prestate []byte, proofData []byte, preimageData *types.PreimageOracleData, err error) {
if len(s.providers) == 0 { ancestorDepth, _ := s.providerForDepth(uint64(pos.Depth()))
return nil, nil, nil, NoProvidersErr relativePosition, err := pos.RelativeToAncestorAtDepth(ancestorDepth)
if err != nil {
return nil, nil, nil, err
} }
return s.providers[len(s.providers)-1].GetStepData(ctx, i) return s.bottomProvider.GetStepData(ctx, relativePosition)
} }
...@@ -23,9 +23,9 @@ func TestGet(t *testing.T) { ...@@ -23,9 +23,9 @@ func TestGet(t *testing.T) {
t.Run("ErrorBubblesUp", func(t *testing.T) { t.Run("ErrorBubblesUp", func(t *testing.T) {
mockOutputProvider := mockTraceProvider{getError: mockGetError} mockOutputProvider := mockTraceProvider{getError: mockGetError}
splitProvider := SplitTraceProvider{ splitProvider := SplitTraceProvider{
logger: testlog.Logger(t, log.LvlInfo), logger: testlog.Logger(t, log.LvlInfo),
providers: []types.TraceProvider{&mockOutputProvider}, topProvider: &mockOutputProvider,
depthTiers: []uint64{40, 20}, topDepth: 40,
} }
_, err := splitProvider.Get(context.Background(), types.NewPosition(1, 0)) _, err := splitProvider.Get(context.Background(), types.NewPosition(1, 0))
require.ErrorIs(t, err, mockGetError) require.ErrorIs(t, err, mockGetError)
...@@ -34,9 +34,9 @@ func TestGet(t *testing.T) { ...@@ -34,9 +34,9 @@ func TestGet(t *testing.T) {
t.Run("ReturnsCorrectOutput", func(t *testing.T) { t.Run("ReturnsCorrectOutput", func(t *testing.T) {
mockOutputProvider := mockTraceProvider{getOutput: mockOutput} mockOutputProvider := mockTraceProvider{getOutput: mockOutput}
splitProvider := SplitTraceProvider{ splitProvider := SplitTraceProvider{
logger: testlog.Logger(t, log.LvlInfo), logger: testlog.Logger(t, log.LvlInfo),
providers: []types.TraceProvider{&mockOutputProvider}, topProvider: &mockOutputProvider,
depthTiers: []uint64{40, 20}, topDepth: 40,
} }
output, err := splitProvider.Get(context.Background(), types.NewPosition(6, 3)) output, err := splitProvider.Get(context.Background(), types.NewPosition(6, 3))
require.NoError(t, err) require.NoError(t, err)
...@@ -45,12 +45,13 @@ func TestGet(t *testing.T) { ...@@ -45,12 +45,13 @@ func TestGet(t *testing.T) {
}) })
t.Run("ReturnsCorrectOutputWithMultipleProviders", func(t *testing.T) { t.Run("ReturnsCorrectOutputWithMultipleProviders", func(t *testing.T) {
firstOutputProvider := mockTraceProvider{} topProvider := mockTraceProvider{}
secondOutputProvider := mockTraceProvider{getOutput: mockOutput} bottomProvider := mockTraceProvider{getOutput: mockOutput}
splitProvider := SplitTraceProvider{ splitProvider := SplitTraceProvider{
logger: testlog.Logger(t, log.LvlInfo), logger: testlog.Logger(t, log.LvlInfo),
providers: []types.TraceProvider{&firstOutputProvider, &secondOutputProvider}, topProvider: &topProvider,
depthTiers: []uint64{40, 20}, bottomProvider: &bottomProvider,
topDepth: 40,
} }
output, err := splitProvider.Get(context.Background(), types.NewPosition(42, 17)) output, err := splitProvider.Get(context.Background(), types.NewPosition(42, 17))
require.NoError(t, err) require.NoError(t, err)
...@@ -63,9 +64,9 @@ func TestAbsolutePreStateCommitment(t *testing.T) { ...@@ -63,9 +64,9 @@ func TestAbsolutePreStateCommitment(t *testing.T) {
t.Run("ErrorBubblesUp", func(t *testing.T) { t.Run("ErrorBubblesUp", func(t *testing.T) {
mockOutputProvider := mockTraceProvider{absolutePreStateCommitmentError: mockGetError} mockOutputProvider := mockTraceProvider{absolutePreStateCommitmentError: mockGetError}
splitProvider := SplitTraceProvider{ splitProvider := SplitTraceProvider{
logger: testlog.Logger(t, log.LvlInfo), logger: testlog.Logger(t, log.LvlInfo),
providers: []types.TraceProvider{&mockOutputProvider}, bottomProvider: &mockOutputProvider,
depthTiers: []uint64{40, 20}, topDepth: 40,
} }
_, err := splitProvider.AbsolutePreStateCommitment(context.Background()) _, err := splitProvider.AbsolutePreStateCommitment(context.Background())
require.ErrorIs(t, err, mockGetError) require.ErrorIs(t, err, mockGetError)
...@@ -74,9 +75,9 @@ func TestAbsolutePreStateCommitment(t *testing.T) { ...@@ -74,9 +75,9 @@ func TestAbsolutePreStateCommitment(t *testing.T) {
t.Run("ReturnsCorrectOutput", func(t *testing.T) { t.Run("ReturnsCorrectOutput", func(t *testing.T) {
mockOutputProvider := mockTraceProvider{absolutePreStateCommitment: mockCommitment} mockOutputProvider := mockTraceProvider{absolutePreStateCommitment: mockCommitment}
splitProvider := SplitTraceProvider{ splitProvider := SplitTraceProvider{
logger: testlog.Logger(t, log.LvlInfo), logger: testlog.Logger(t, log.LvlInfo),
providers: []types.TraceProvider{&mockOutputProvider}, bottomProvider: &mockOutputProvider,
depthTiers: []uint64{40, 20}, topDepth: 40,
} }
output, err := splitProvider.AbsolutePreStateCommitment(context.Background()) output, err := splitProvider.AbsolutePreStateCommitment(context.Background())
require.NoError(t, err) require.NoError(t, err)
...@@ -88,9 +89,9 @@ func TestAbsolutePreState(t *testing.T) { ...@@ -88,9 +89,9 @@ func TestAbsolutePreState(t *testing.T) {
t.Run("ErrorBubblesUp", func(t *testing.T) { t.Run("ErrorBubblesUp", func(t *testing.T) {
mockOutputProvider := mockTraceProvider{absolutePreStateError: mockGetError} mockOutputProvider := mockTraceProvider{absolutePreStateError: mockGetError}
splitProvider := SplitTraceProvider{ splitProvider := SplitTraceProvider{
logger: testlog.Logger(t, log.LvlInfo), logger: testlog.Logger(t, log.LvlInfo),
providers: []types.TraceProvider{&mockOutputProvider}, bottomProvider: &mockOutputProvider,
depthTiers: []uint64{40}, topDepth: 40,
} }
_, err := splitProvider.AbsolutePreState(context.Background()) _, err := splitProvider.AbsolutePreState(context.Background())
require.ErrorIs(t, err, mockGetError) require.ErrorIs(t, err, mockGetError)
...@@ -101,9 +102,9 @@ func TestGetStepData(t *testing.T) { ...@@ -101,9 +102,9 @@ func TestGetStepData(t *testing.T) {
t.Run("ErrorBubblesUp", func(t *testing.T) { t.Run("ErrorBubblesUp", func(t *testing.T) {
mockOutputProvider := mockTraceProvider{getStepDataError: mockGetError} mockOutputProvider := mockTraceProvider{getStepDataError: mockGetError}
splitProvider := SplitTraceProvider{ splitProvider := SplitTraceProvider{
logger: testlog.Logger(t, log.LvlInfo), logger: testlog.Logger(t, log.LvlInfo),
providers: []types.TraceProvider{&mockOutputProvider}, bottomProvider: &mockOutputProvider,
depthTiers: []uint64{40}, topDepth: 40,
} }
_, _, _, err := splitProvider.GetStepData(context.Background(), types.NewPosition(0, 0)) _, _, _, err := splitProvider.GetStepData(context.Background(), types.NewPosition(0, 0))
require.ErrorIs(t, err, mockGetError) require.ErrorIs(t, err, mockGetError)
......
package types package types
import "fmt" import (
"errors"
"fmt"
)
var (
PositionDepthTooSmall = 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.
type Position struct { type Position struct {
...@@ -25,13 +32,16 @@ func (p Position) MoveRight() Position { ...@@ -25,13 +32,16 @@ func (p Position) MoveRight() Position {
} }
} }
// Localize returns a new position for a subtree. // RelativeToAncestorAtDepth returns a new position for a subtree.
// reduced is the number of levels to reduce the depth by. // [ancestor] is the depth of the subtree root node.
func (p Position) Localize(reduced uint64) Position { func (p Position) RelativeToAncestorAtDepth(ancestor uint64) (Position, error) {
newPosDepth := uint64(p.depth) - reduced if ancestor > uint64(p.depth) {
return Position{}, PositionDepthTooSmall
}
newPosDepth := uint64(p.depth) - ancestor
nodesAtDepth := 1 << newPosDepth nodesAtDepth := 1 << newPosDepth
newIndexAtDepth := p.indexAtDepth % nodesAtDepth newIndexAtDepth := p.indexAtDepth % nodesAtDepth
return NewPosition(int(newPosDepth), newIndexAtDepth) return NewPosition(int(newPosDepth), newIndexAtDepth), nil
} }
func (p Position) Depth() int { func (p Position) Depth() int {
......
...@@ -119,3 +119,19 @@ func TestDefend(t *testing.T) { ...@@ -119,3 +119,19 @@ func TestDefend(t *testing.T) {
require.Equalf(t, test.DefendGIndex, result.ToGIndex(), "Defend from GIndex %v", pos.ToGIndex()) require.Equalf(t, test.DefendGIndex, result.ToGIndex(), "Defend from GIndex %v", pos.ToGIndex())
} }
} }
func TestRelativeToAncestorAtDepth(t *testing.T) {
t.Run("ErrorsForDeepAncestor", func(t *testing.T) {
pos := NewPosition(1, 1)
_, err := pos.RelativeToAncestorAtDepth(2)
require.ErrorIs(t, err, PositionDepthTooSmall)
})
t.Run("Success", func(t *testing.T) {
pos := NewPosition(2, 1)
expectedRelativePosition := NewPosition(1, 1)
relativePosition, err := pos.RelativeToAncestorAtDepth(1)
require.NoError(t, err)
require.Equal(t, expectedRelativePosition, relativePosition)
})
}
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