Commit 639363c2 authored by Adrian Sutton's avatar Adrian Sutton

op-challenger: Add BlockNumber method to OutputTraceProvider

Will allow the split provider to convert a claim position to the L2 block number.
Also remove duplication and improve tests.
parent 587999a7
...@@ -2,6 +2,7 @@ package outputs ...@@ -2,6 +2,7 @@ package outputs
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/types" "github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
...@@ -13,8 +14,8 @@ import ( ...@@ -13,8 +14,8 @@ import (
) )
var ( var (
GetStepDataErr = fmt.Errorf("GetStepData not supported") ErrGetStepData = errors.New("GetStepData not supported")
AbsolutePreStateErr = fmt.Errorf("AbsolutePreState not supported") ErrIndexTooBig = errors.New("trace index is greater than max uint64")
) )
var _ types.TraceProvider = (*OutputTraceProvider)(nil) var _ types.TraceProvider = (*OutputTraceProvider)(nil)
...@@ -51,39 +52,40 @@ func NewTraceProviderFromInputs(logger log.Logger, rollupClient OutputRollupClie ...@@ -51,39 +52,40 @@ func NewTraceProviderFromInputs(logger log.Logger, rollupClient OutputRollupClie
} }
} }
func (o *OutputTraceProvider) Get(ctx context.Context, pos types.Position) (common.Hash, error) { func (o *OutputTraceProvider) BlockNumber(pos types.Position) (uint64, error) {
traceIndex := pos.TraceIndex(int(o.gameDepth)) traceIndex := pos.TraceIndex(int(o.gameDepth))
if !traceIndex.IsUint64() { if !traceIndex.IsUint64() {
return common.Hash{}, fmt.Errorf("trace index %v is greater than max uint64", traceIndex) return 0, fmt.Errorf("%w: %v", ErrIndexTooBig, traceIndex)
} }
outputBlock := traceIndex.Uint64() + o.prestateBlock + 1 outputBlock := traceIndex.Uint64() + o.prestateBlock + 1
if outputBlock > o.poststateBlock { if outputBlock > o.poststateBlock {
outputBlock = o.poststateBlock outputBlock = o.poststateBlock
} }
output, err := o.rollupClient.OutputAtBlock(ctx, outputBlock) return outputBlock, nil
}
func (o *OutputTraceProvider) Get(ctx context.Context, pos types.Position) (common.Hash, error) {
outputBlock, err := o.BlockNumber(pos)
if err != nil { if err != nil {
o.logger.Error("Failed to fetch output", "blockNumber", outputBlock, "err", err)
return common.Hash{}, err return common.Hash{}, err
} }
return common.Hash(output.OutputRoot), nil return o.outputAtBlock(ctx, outputBlock)
} }
// AbsolutePreStateCommitment returns the absolute prestate at the configured prestateBlock. // AbsolutePreStateCommitment returns the absolute prestate at the configured prestateBlock.
func (o *OutputTraceProvider) AbsolutePreStateCommitment(ctx context.Context) (hash common.Hash, err error) { func (o *OutputTraceProvider) AbsolutePreStateCommitment(ctx context.Context) (hash common.Hash, err error) {
output, err := o.rollupClient.OutputAtBlock(ctx, o.prestateBlock) return o.outputAtBlock(ctx, o.prestateBlock)
if err != nil {
o.logger.Error("Failed to fetch output", "blockNumber", o.prestateBlock, "err", err)
return common.Hash{}, err
}
return common.Hash(output.OutputRoot), nil
} }
// AbsolutePreState is not supported in the [OutputTraceProvider]. // GetStepData is not supported in the [OutputTraceProvider].
func (o *OutputTraceProvider) AbsolutePreState(ctx context.Context) (preimage []byte, err error) { func (o *OutputTraceProvider) GetStepData(_ context.Context, _ types.Position) (prestate []byte, proofData []byte, preimageData *types.PreimageOracleData, err error) {
return nil, AbsolutePreStateErr return nil, nil, nil, ErrGetStepData
} }
// GetStepData is not supported in the [OutputTraceProvider]. func (o *OutputTraceProvider) outputAtBlock(ctx context.Context, block uint64) (common.Hash, error) {
func (o *OutputTraceProvider) GetStepData(ctx context.Context, pos types.Position) (prestate []byte, proofData []byte, preimageData *types.PreimageOracleData, err error) { output, err := o.rollupClient.OutputAtBlock(ctx, block)
return nil, nil, nil, GetStepDataErr if err != nil {
return common.Hash{}, fmt.Errorf("failed to fetch output at block %v: %w", o.prestateBlock, err)
}
return common.Hash(output.OutputRoot), nil
} }
...@@ -2,6 +2,7 @@ package outputs ...@@ -2,6 +2,7 @@ package outputs
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"math/big" "math/big"
"testing" "testing"
...@@ -9,7 +10,6 @@ import ( ...@@ -9,7 +10,6 @@ import (
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/types" "github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
"github.com/ethereum-optimism/optimism/op-service/eth" "github.com/ethereum-optimism/optimism/op-service/eth"
"github.com/ethereum-optimism/optimism/op-service/testlog" "github.com/ethereum-optimism/optimism/op-service/testlog"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
...@@ -22,40 +22,29 @@ var ( ...@@ -22,40 +22,29 @@ var (
prestateOutputRoot = common.HexToHash("0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") prestateOutputRoot = common.HexToHash("0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
firstOutputRoot = common.HexToHash("0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb") firstOutputRoot = common.HexToHash("0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb")
poststateOutputRoot = common.HexToHash("0xcccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc") poststateOutputRoot = common.HexToHash("0xcccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc")
errNoOutputAtBlock = errors.New("no output at block")
) )
func TestGet(t *testing.T) { func TestGet(t *testing.T) {
t.Run("PrePrestateErrors", func(t *testing.T) {
provider, _ := setupWithTestData(t, 0, poststateBlock)
_, err := provider.Get(context.Background(), types.NewPosition(1, common.Big0))
require.ErrorAs(t, fmt.Errorf("no output at block %d", 1), &err)
})
t.Run("ErrorsTraceIndexOutOfBounds", func(t *testing.T) { t.Run("ErrorsTraceIndexOutOfBounds", func(t *testing.T) {
deepGame := uint64(64) deepGame := uint64(164)
provider, _ := setupWithTestData(t, prestateBlock, poststateBlock, deepGame) provider, _ := setupWithTestData(t, prestateBlock, poststateBlock, deepGame)
pos := types.NewPosition(0, big.NewInt(0)) pos := types.NewPosition(0, big.NewInt(0))
_, err := provider.Get(context.Background(), pos) _, err := provider.Get(context.Background(), pos)
require.ErrorAs(t, fmt.Errorf("trace index %v is greater than max uint64", pos.TraceIndex(int(deepGame))), &err) require.ErrorIs(t, err, ErrIndexTooBig)
})
t.Run("MisconfiguredPoststateErrors", func(t *testing.T) {
provider, _ := setupWithTestData(t, 0, 0)
_, err := provider.Get(context.Background(), types.NewPosition(1, common.Big0))
require.ErrorAs(t, fmt.Errorf("no output at block %d", 0), &err)
}) })
t.Run("FirstBlockAfterPrestate", func(t *testing.T) { t.Run("FirstBlockAfterPrestate", func(t *testing.T) {
provider, _ := setupWithTestData(t, prestateBlock, poststateBlock) provider, _ := setupWithTestData(t, prestateBlock, poststateBlock)
value, err := provider.Get(context.Background(), types.NewPositionFromGIndex(big.NewInt(128))) value, err := provider.Get(context.Background(), types.NewPosition(int(gameDepth), big.NewInt(0)))
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, firstOutputRoot, value) require.Equal(t, firstOutputRoot, value)
}) })
t.Run("MissingOutputAtBlock", func(t *testing.T) { t.Run("MissingOutputAtBlock", func(t *testing.T) {
provider, _ := setupWithTestData(t, prestateBlock, poststateBlock) provider, _ := setupWithTestData(t, prestateBlock, poststateBlock)
_, err := provider.Get(context.Background(), types.NewPositionFromGIndex(big.NewInt(129))) _, err := provider.Get(context.Background(), types.NewPosition(int(gameDepth), big.NewInt(1)))
require.ErrorAs(t, fmt.Errorf("no output at block %d", prestateBlock+2), &err) require.ErrorIs(t, err, errNoOutputAtBlock)
}) })
t.Run("PostStateBlock", func(t *testing.T) { t.Run("PostStateBlock", func(t *testing.T) {
...@@ -73,12 +62,46 @@ func TestGet(t *testing.T) { ...@@ -73,12 +62,46 @@ func TestGet(t *testing.T) {
}) })
} }
func TestGetBlockNumber(t *testing.T) {
tests := []struct {
name string
pos types.Position
expected uint64
}{
{"FirstBlockAfterPrestate", types.NewPosition(int(gameDepth), big.NewInt(0)), prestateBlock + 1},
{"PostStateBlock", types.NewPositionFromGIndex(big.NewInt(228)), poststateBlock},
{"AfterPostStateBlock", types.NewPositionFromGIndex(big.NewInt(229)), poststateBlock},
{"Root", types.NewPositionFromGIndex(big.NewInt(1)), poststateBlock},
{"MiddleNode1", types.NewPosition(int(gameDepth-1), big.NewInt(2)), 106},
{"MiddleNode2", types.NewPosition(int(gameDepth-1), big.NewInt(3)), 108},
{"Leaf1", types.NewPosition(int(gameDepth), big.NewInt(1)), prestateBlock + 2},
{"Leaf2", types.NewPosition(int(gameDepth), big.NewInt(2)), prestateBlock + 3},
}
for _, test := range tests {
test := test
t.Run(test.name, func(t *testing.T) {
provider, _ := setupWithTestData(t, prestateBlock, poststateBlock)
actual, err := provider.BlockNumber(test.pos)
require.NoError(t, err)
require.Equal(t, test.expected, actual)
})
}
t.Run("ErrorsTraceIndexOutOfBounds", func(t *testing.T) {
deepGame := uint64(164)
provider, _ := setupWithTestData(t, prestateBlock, poststateBlock, deepGame)
pos := types.NewPosition(0, big.NewInt(0))
_, err := provider.BlockNumber(pos)
require.ErrorIs(t, err, ErrIndexTooBig)
})
}
func TestAbsolutePreStateCommitment(t *testing.T) { func TestAbsolutePreStateCommitment(t *testing.T) {
t.Run("FailedToFetchOutput", func(t *testing.T) { t.Run("FailedToFetchOutput", func(t *testing.T) {
provider, rollupClient := setupWithTestData(t, prestateBlock, poststateBlock) provider, rollupClient := setupWithTestData(t, prestateBlock, poststateBlock)
rollupClient.errorsOnPrestateFetch = true rollupClient.errorsOnPrestateFetch = true
_, err := provider.AbsolutePreStateCommitment(context.Background()) _, err := provider.AbsolutePreStateCommitment(context.Background())
require.ErrorAs(t, fmt.Errorf("no output at block %d", prestateBlock), &err) require.ErrorIs(t, err, errNoOutputAtBlock)
}) })
t.Run("ReturnsCorrectPrestateOutput", func(t *testing.T) { t.Run("ReturnsCorrectPrestateOutput", func(t *testing.T) {
...@@ -92,13 +115,7 @@ func TestAbsolutePreStateCommitment(t *testing.T) { ...@@ -92,13 +115,7 @@ func TestAbsolutePreStateCommitment(t *testing.T) {
func TestGetStepData(t *testing.T) { func TestGetStepData(t *testing.T) {
provider, _ := setupWithTestData(t, prestateBlock, poststateBlock) provider, _ := setupWithTestData(t, prestateBlock, poststateBlock)
_, _, _, err := provider.GetStepData(context.Background(), types.NewPosition(1, common.Big0)) _, _, _, err := provider.GetStepData(context.Background(), types.NewPosition(1, common.Big0))
require.ErrorIs(t, err, GetStepDataErr) require.ErrorIs(t, err, ErrGetStepData)
}
func TestAbsolutePreState(t *testing.T) {
provider, _ := setupWithTestData(t, prestateBlock, poststateBlock)
_, err := provider.AbsolutePreState(context.Background())
require.ErrorIs(t, err, AbsolutePreStateErr)
} }
func setupWithTestData(t *testing.T, prestateBlock, poststateBlock uint64, customGameDepth ...uint64) (*OutputTraceProvider, *stubRollupClient) { func setupWithTestData(t *testing.T, prestateBlock, poststateBlock uint64, customGameDepth ...uint64) (*OutputTraceProvider, *stubRollupClient) {
...@@ -133,10 +150,10 @@ type stubRollupClient struct { ...@@ -133,10 +150,10 @@ type stubRollupClient struct {
outputs map[uint64]*eth.OutputResponse outputs map[uint64]*eth.OutputResponse
} }
func (s *stubRollupClient) OutputAtBlock(ctx context.Context, blockNum uint64) (*eth.OutputResponse, error) { func (s *stubRollupClient) OutputAtBlock(_ context.Context, blockNum uint64) (*eth.OutputResponse, error) {
output, ok := s.outputs[blockNum] output, ok := s.outputs[blockNum]
if !ok || s.errorsOnPrestateFetch { if !ok || s.errorsOnPrestateFetch {
return nil, fmt.Errorf("no output at block %d", blockNum) return nil, fmt.Errorf("%w: %d", errNoOutputAtBlock, blockNum)
} }
return output, nil return output, 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