Commit 6774f533 authored by Andreas Bigger's avatar Andreas Bigger

Introduces the Output Provider over L2 Block output roots.

parent 6ab378e4
package outputs
import (
"context"
"fmt"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
"github.com/ethereum-optimism/optimism/op-service/client"
"github.com/ethereum-optimism/optimism/op-service/eth"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/log"
)
var (
GetStepDataErr = fmt.Errorf("GetStepData not supported")
AbsolutePreStateErr = fmt.Errorf("AbsolutePreState not supported")
PreStateRequestErr = fmt.Errorf("Requested trace index is before prestate block")
)
type OutputRollupClient interface {
OutputAtBlock(ctx context.Context, blockNum uint64) (*eth.OutputResponse, error)
}
// OutputTraceProvider is a [types.TraceProvider] implementation that uses
// output roots for given L2 Blocks as a trace.
type OutputTraceProvider struct {
logger log.Logger
rollupClient OutputRollupClient
prestateBlock uint64
}
func NewTraceProvider(ctx context.Context, logger log.Logger, rollupRpc string, prestateBlock uint64) (*OutputTraceProvider, error) {
rollupClient, err := client.DialRollupClientWithTimeout(client.DefaultDialTimeout, logger, rollupRpc)
if err != nil {
return nil, err
}
return NewTraceProviderFromInputs(logger, rollupClient, prestateBlock), nil
}
func NewTraceProviderFromInputs(logger log.Logger, rollupClient OutputRollupClient, prestateBlock uint64) *OutputTraceProvider {
return &OutputTraceProvider{
logger: logger,
rollupClient: rollupClient,
prestateBlock: prestateBlock,
}
}
func (o *OutputTraceProvider) Get(ctx context.Context, traceIndex uint64) (common.Hash, error) {
outputBlock := traceIndex + 1
if outputBlock < o.prestateBlock {
return common.Hash{}, PreStateRequestErr
}
output, err := o.rollupClient.OutputAtBlock(ctx, outputBlock)
if err != nil {
o.logger.Error("Failed to fetch output", "blockNumber", outputBlock, "err", err)
return common.Hash{}, err
}
return common.Hash(output.OutputRoot), nil
}
// AbsolutePreStateCommitment returns the absolute prestate at the configured prestateBlock.
// For Optimism Mainnet, this is the first block after bedrock.
func (o *OutputTraceProvider) AbsolutePreStateCommitment(ctx context.Context) (hash common.Hash, err error) {
output, err := o.rollupClient.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].
func (o *OutputTraceProvider) AbsolutePreState(ctx context.Context) (preimage []byte, err error) {
return nil, AbsolutePreStateErr
}
// GetStepData is not supported in the [OutputTraceProvider].
func (o *OutputTraceProvider) GetStepData(ctx context.Context, i uint64) (prestate []byte, proofData []byte, preimageData *types.PreimageOracleData, err error) {
return nil, nil, nil, GetStepDataErr
}
package outputs
import (
"context"
"fmt"
"testing"
"github.com/ethereum-optimism/optimism/op-node/testlog"
"github.com/ethereum-optimism/optimism/op-service/eth"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/log"
"github.com/stretchr/testify/require"
)
var (
prestateBlock = uint64(100)
prestateOutputRoot = common.HexToHash("0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
firstOutputRoot = common.HexToHash("0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb")
)
func TestGet(t *testing.T) {
t.Run("TraceIndexBeforePrestate", func(t *testing.T) {
provider, _ := setupWithTestData(t, prestateBlock)
_, err := provider.Get(context.Background(), 0)
require.ErrorIs(t, err, PreStateRequestErr)
})
t.Run("MissingOutputAtBlock", func(t *testing.T) {
provider, _ := setupWithTestData(t, prestateBlock)
traceIndex := 101
_, err := provider.Get(context.Background(), uint64(traceIndex))
require.ErrorAs(t, fmt.Errorf("no output at block %d", uint64(traceIndex+1)), &err)
})
t.Run("Success", func(t *testing.T) {
provider, _ := setupWithTestData(t, prestateBlock)
value, err := provider.Get(context.Background(), 100)
require.NoError(t, err)
require.Equal(t, value, firstOutputRoot)
})
}
func TestAbsolutePreStateCommitment(t *testing.T) {
t.Run("FailedToFetchOutput", func(t *testing.T) {
provider, rollupClient := setupWithTestData(t, prestateBlock)
rollupClient.errorsOnPrestateFetch = true
_, err := provider.AbsolutePreStateCommitment(context.Background())
require.ErrorAs(t, fmt.Errorf("no output at block %d", prestateBlock), &err)
})
t.Run("Success", func(t *testing.T) {
provider, _ := setupWithTestData(t, prestateBlock)
value, err := provider.AbsolutePreStateCommitment(context.Background())
require.NoError(t, err)
require.Equal(t, value, prestateOutputRoot)
})
}
func TestGetStepData(t *testing.T) {
provider, _ := setupWithTestData(t, prestateBlock)
_, _, _, err := provider.GetStepData(context.Background(), 0)
require.ErrorIs(t, err, GetStepDataErr)
}
func TestAbsolutePreState(t *testing.T) {
provider, _ := setupWithTestData(t, prestateBlock)
_, err := provider.AbsolutePreState(context.Background())
require.ErrorIs(t, err, AbsolutePreStateErr)
}
func setupWithTestData(t *testing.T, prestateBlock uint64) (*OutputTraceProvider, *stubRollupClient) {
rollupClient := stubRollupClient{
outputs: map[uint64]*eth.OutputResponse{
100: {
OutputRoot: eth.Bytes32(prestateOutputRoot),
},
101: {
OutputRoot: eth.Bytes32(firstOutputRoot),
},
},
}
return &OutputTraceProvider{
logger: testlog.Logger(t, log.LvlInfo),
rollupClient: &rollupClient,
prestateBlock: prestateBlock,
}, &rollupClient
}
type stubRollupClient struct {
errorsOnPrestateFetch bool
outputs map[uint64]*eth.OutputResponse
}
func (s *stubRollupClient) OutputAtBlock(ctx context.Context, blockNum uint64) (*eth.OutputResponse, error) {
output, ok := s.outputs[blockNum]
if !ok || s.errorsOnPrestateFetch {
return nil, fmt.Errorf("no output at block %d", blockNum)
}
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