Commit 181c74a3 authored by Andreas Bigger's avatar Andreas Bigger

Implements the split trace provider.

parent 546fb2c7
package split
import (
"context"
"fmt"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/log"
)
var (
GetStepDataErr = fmt.Errorf("GetStepData not supported")
NoProvidersErr = fmt.Errorf("no trace providers configured")
)
// todo(refcell): the Get method traceIndex is update to be a Position so we have the depth
// var _ types.TraceProvider = (*SplitTraceProvider)(nil)
// SplitTraceProvider is a [types.TraceProvider] implementation that
// routes requests to the correct internal trace provider based on the
// depth of the requested trace.
type SplitTraceProvider struct {
logger log.Logger
providers []types.TraceProvider
depthTiers []uint64
}
func NewTraceProvider(logger log.Logger, providers []types.TraceProvider, depthTiers []uint64) *SplitTraceProvider {
return &SplitTraceProvider{
logger: logger,
providers: providers,
depthTiers: depthTiers,
}
}
func (s *SplitTraceProvider) providerForDepth(depth uint64) types.TraceProvider {
for i, tier := range s.depthTiers {
if depth <= tier {
return s.providers[i]
}
}
return s.providers[len(s.providers)-1]
}
// Get routes the Get request to the internal [types.TraceProvider] that
// that serves the trace index at the depth.
func (s *SplitTraceProvider) Get(ctx context.Context, pos types.Position) (common.Hash, error) {
if len(s.providers) == 0 {
return common.Hash{}, NoProvidersErr
}
return s.providerForDepth(uint64(pos.Depth())).Get(ctx, pos.TraceIndex(pos.Depth()))
}
// AbsolutePreStateCommitment returns the absolute prestate from the lowest internal [types.TraceProvider]
func (s *SplitTraceProvider) AbsolutePreStateCommitment(ctx context.Context) (hash common.Hash, err error) {
if len(s.providers) == 0 {
return common.Hash{}, NoProvidersErr
}
return s.providers[len(s.providers)-1].AbsolutePreStateCommitment(ctx)
}
// AbsolutePreState routes the AbsolutePreState request to the lowest internal [types.TraceProvider].
func (s *SplitTraceProvider) AbsolutePreState(ctx context.Context) (preimage []byte, err error) {
if len(s.providers) == 0 {
return nil, NoProvidersErr
}
return s.providers[len(s.providers)-1].AbsolutePreState(ctx)
}
// GetStepData routes the GetStepData request to the lowest internal [types.TraceProvider].
func (s *SplitTraceProvider) GetStepData(ctx context.Context, i uint64) (prestate []byte, proofData []byte, preimageData *types.PreimageOracleData, err error) {
if len(s.providers) == 0 {
return nil, nil, nil, NoProvidersErr
}
return s.providers[len(s.providers)-1].GetStepData(ctx, i)
}
package split
import (
"context"
"fmt"
"testing"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
"github.com/ethereum-optimism/optimism/op-node/testlog"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/log"
"github.com/stretchr/testify/require"
)
var (
mockGetError = fmt.Errorf("mock get error")
mockOutput = common.HexToHash("0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
mockCommitment = common.HexToHash("0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb")
)
func TestGet(t *testing.T) {
t.Run("ErrorBubblesUp", func(t *testing.T) {
mockOutputProvider := mockTraceProvider{getError: mockGetError}
splitProvider := SplitTraceProvider{
logger: testlog.Logger(t, log.LvlInfo),
providers: []types.TraceProvider{&mockOutputProvider},
depthTiers: []uint64{40, 20},
}
_, err := splitProvider.Get(context.Background(), types.NewPosition(1, 0))
require.ErrorIs(t, err, mockGetError)
})
t.Run("ReturnsCorrectOutput", func(t *testing.T) {
mockOutputProvider := mockTraceProvider{getOutput: mockOutput}
splitProvider := SplitTraceProvider{
logger: testlog.Logger(t, log.LvlInfo),
providers: []types.TraceProvider{&mockOutputProvider},
depthTiers: []uint64{40, 20},
}
output, err := splitProvider.Get(context.Background(), types.NewPosition(1, 0))
require.NoError(t, err)
require.Equal(t, mockOutput, output)
})
t.Run("ReturnsCorrectOutputWithMultipleProviders", func(t *testing.T) {
firstOutputProvider := mockTraceProvider{}
secondOutputProvider := mockTraceProvider{getOutput: mockOutput}
splitProvider := SplitTraceProvider{
logger: testlog.Logger(t, log.LvlInfo),
providers: []types.TraceProvider{&firstOutputProvider, &secondOutputProvider},
depthTiers: []uint64{40, 20},
}
output, err := splitProvider.Get(context.Background(), types.NewPosition(41, 0))
require.NoError(t, err)
require.Equal(t, mockOutput, output)
})
}
func TestAbsolutePreStateCommitment(t *testing.T) {
t.Run("ErrorBubblesUp", func(t *testing.T) {
mockOutputProvider := mockTraceProvider{absolutePreStateCommitmentError: mockGetError}
splitProvider := SplitTraceProvider{
logger: testlog.Logger(t, log.LvlInfo),
providers: []types.TraceProvider{&mockOutputProvider},
depthTiers: []uint64{40, 20},
}
_, err := splitProvider.AbsolutePreStateCommitment(context.Background())
require.ErrorIs(t, err, mockGetError)
})
t.Run("ReturnsCorrectOutput", func(t *testing.T) {
mockOutputProvider := mockTraceProvider{absolutePreStateCommitment: mockCommitment}
splitProvider := SplitTraceProvider{
logger: testlog.Logger(t, log.LvlInfo),
providers: []types.TraceProvider{&mockOutputProvider},
depthTiers: []uint64{40, 20},
}
output, err := splitProvider.AbsolutePreStateCommitment(context.Background())
require.NoError(t, err)
require.Equal(t, mockCommitment, output)
})
}
func TestAbsolutePreState(t *testing.T) {
t.Run("ErrorBubblesUp", func(t *testing.T) {
mockOutputProvider := mockTraceProvider{absolutePreStateError: mockGetError}
splitProvider := SplitTraceProvider{
logger: testlog.Logger(t, log.LvlInfo),
providers: []types.TraceProvider{&mockOutputProvider},
depthTiers: []uint64{40},
}
_, err := splitProvider.AbsolutePreState(context.Background())
require.ErrorIs(t, err, mockGetError)
})
}
func TestGetStepData(t *testing.T) {
t.Run("ErrorBubblesUp", func(t *testing.T) {
mockOutputProvider := mockTraceProvider{getStepDataError: mockGetError}
splitProvider := SplitTraceProvider{
logger: testlog.Logger(t, log.LvlInfo),
providers: []types.TraceProvider{&mockOutputProvider},
depthTiers: []uint64{40},
}
_, _, _, err := splitProvider.GetStepData(context.Background(), 0)
require.ErrorIs(t, err, mockGetError)
})
}
type mockTraceProvider struct {
getOutput common.Hash
getError error
absolutePreStateCommitmentError error
absolutePreStateCommitment common.Hash
absolutePreStateError error
getStepDataError error
}
func (m *mockTraceProvider) Get(ctx context.Context, i uint64) (common.Hash, error) {
if m.getError != nil {
return common.Hash{}, m.getError
}
return m.getOutput, nil
}
func (m *mockTraceProvider) AbsolutePreStateCommitment(ctx context.Context) (hash common.Hash, err error) {
if m.absolutePreStateCommitmentError != nil {
return common.Hash{}, m.absolutePreStateCommitmentError
}
return m.absolutePreStateCommitment, nil
}
func (m *mockTraceProvider) AbsolutePreState(ctx context.Context) (preimage []byte, err error) {
if m.absolutePreStateError != nil {
return []byte{}, m.absolutePreStateError
}
return []byte{}, nil
}
func (m *mockTraceProvider) GetStepData(ctx context.Context, i uint64) ([]byte, []byte, *types.PreimageOracleData, error) {
if m.getStepDataError != nil {
return nil, nil, nil, m.getStepDataError
}
return nil, nil, nil, nil
}
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