Commit 78badddf authored by Adrian Sutton's avatar Adrian Sutton Committed by GitHub

op-challenger: Refactor super trace provider to supply preimages (#13796)

Add prestate provider for super roots that can provide preimages.
parent 0a7d1a61
package super
import "github.com/ethereum-optimism/optimism/op-service/eth"
func responseToSuper(prevRoot eth.SuperRootResponse) *eth.SuperV1 {
prevChainOutputs := make([]eth.ChainIDAndOutput, 0, len(prevRoot.Chains))
for _, chain := range prevRoot.Chains {
prevChainOutputs = append(prevChainOutputs, eth.ChainIDAndOutput{ChainID: chain.ChainID.ToBig().Uint64(), Output: chain.Canonical})
}
superV1 := eth.NewSuperV1(prevRoot.Timestamp, prevChainOutputs...)
return superV1
}
package super
import (
"testing"
"github.com/ethereum-optimism/optimism/op-service/eth"
"github.com/stretchr/testify/require"
)
func TestResponseToSuper(t *testing.T) {
t.Run("SingleChain", func(t *testing.T) {
input := eth.SuperRootResponse{
Timestamp: 4978924,
SuperRoot: eth.Bytes32{0x65},
Chains: []eth.ChainRootInfo{
{
ChainID: eth.ChainID{2987},
Canonical: eth.Bytes32{0x88},
Pending: []byte{1, 2, 3, 4, 5},
},
},
}
expected := &eth.SuperV1{
Timestamp: 4978924,
Chains: []eth.ChainIDAndOutput{
{ChainID: 2987, Output: eth.Bytes32{0x88}},
},
}
actual := responseToSuper(input)
require.Equal(t, expected, actual)
})
t.Run("SortChainsByChainID", func(t *testing.T) {
input := eth.SuperRootResponse{
Timestamp: 4978924,
SuperRoot: eth.Bytes32{0x65},
Chains: []eth.ChainRootInfo{
{
ChainID: eth.ChainID{2987},
Canonical: eth.Bytes32{0x88},
Pending: []byte{1, 2, 3, 4, 5},
},
{
ChainID: eth.ChainID{100},
Canonical: eth.Bytes32{0x10},
Pending: []byte{1, 2, 3, 4, 5},
},
},
}
expected := &eth.SuperV1{
Timestamp: 4978924,
Chains: []eth.ChainIDAndOutput{
{ChainID: 100, Output: eth.Bytes32{0x10}},
{ChainID: 2987, Output: eth.Bytes32{0x88}},
},
}
actual := responseToSuper(input)
require.Equal(t, expected, actual)
})
}
package super
import (
"context"
"github.com/ethereum-optimism/optimism/op-service/eth"
"github.com/ethereum/go-ethereum/common"
)
type SuperRootPrestateProvider struct {
provider RootProvider
timestamp uint64
}
var _ PreimagePrestateProvider = (*SuperRootPrestateProvider)(nil)
func NewSuperRootPrestateProvider(provider RootProvider, prestateTimestamp uint64) *SuperRootPrestateProvider {
return &SuperRootPrestateProvider{
provider: provider,
timestamp: prestateTimestamp,
}
}
func (s *SuperRootPrestateProvider) AbsolutePreStateCommitment(ctx context.Context) (common.Hash, error) {
prestate, err := s.AbsolutePreState(ctx)
if err != nil {
return common.Hash{}, err
}
return common.Hash(eth.SuperRoot(prestate)), nil
}
func (s *SuperRootPrestateProvider) AbsolutePreState(ctx context.Context) (eth.Super, error) {
response, err := s.provider.SuperRootAtTimestamp(ctx, s.timestamp)
if err != nil {
return nil, err
}
return responseToSuper(response), nil
}
package super
import (
"context"
"testing"
"github.com/ethereum-optimism/optimism/op-service/eth"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common"
"github.com/stretchr/testify/require"
)
func TestAbsolutePreState(t *testing.T) {
t.Run("FailedToFetchOutput", func(t *testing.T) {
rootProvider := &stubRootProvider{}
provider := NewSuperRootPrestateProvider(rootProvider, 100)
_, err := provider.AbsolutePreState(context.Background())
require.ErrorIs(t, err, ethereum.NotFound)
_, err = provider.AbsolutePreStateCommitment(context.Background())
require.ErrorIs(t, err, ethereum.NotFound)
})
t.Run("ReturnsSuperRootForTimestamp", func(t *testing.T) {
response := eth.SuperRootResponse{
Timestamp: 100,
SuperRoot: eth.Bytes32{0x11},
Chains: []eth.ChainRootInfo{
{
ChainID: eth.ChainID{2987},
Canonical: eth.Bytes32{0x88},
Pending: []byte{1, 2, 3, 4, 5},
},
{
ChainID: eth.ChainID{100},
Canonical: eth.Bytes32{0x10},
Pending: []byte{1, 2, 3, 4, 5},
},
},
}
expectedPreimage := responseToSuper(response)
rootProvider := &stubRootProvider{
rootsByTimestamp: map[uint64]eth.SuperRootResponse{
100: response,
},
}
provider := NewSuperRootPrestateProvider(rootProvider, 100)
preimage, err := provider.AbsolutePreState(context.Background())
require.NoError(t, err)
require.Equal(t, expectedPreimage, preimage)
commitment, err := provider.AbsolutePreStateCommitment(context.Background())
require.NoError(t, err)
require.Equal(t, common.Hash(eth.SuperRoot(expectedPreimage)), commitment)
})
}
......@@ -25,12 +25,16 @@ const (
StepsPerTimestamp = 1024
)
type PreimagePrestateProvider interface {
types.PrestateProvider
AbsolutePreState(ctx context.Context) (eth.Super, error)
}
type RootProvider interface {
SuperRootAtTimestamp(timestamp uint64) (eth.SuperRootResponse, error)
SuperRootAtTimestamp(ctx context.Context, timestamp uint64) (eth.SuperRootResponse, error)
}
type SuperTraceProvider struct {
types.PrestateProvider
PreimagePrestateProvider
logger log.Logger
rootProvider RootProvider
prestateTimestamp uint64
......@@ -39,66 +43,71 @@ type SuperTraceProvider struct {
gameDepth types.Depth
}
func NewSuperTraceProvider(logger log.Logger, prestateProvider types.PrestateProvider, rootProvider RootProvider, l1Head eth.BlockID, gameDepth types.Depth, prestateTimestamp, poststateTimestamp uint64) *SuperTraceProvider {
func NewSuperTraceProvider(logger log.Logger, prestateProvider PreimagePrestateProvider, rootProvider RootProvider, l1Head eth.BlockID, gameDepth types.Depth, prestateTimestamp, poststateTimestamp uint64) *SuperTraceProvider {
return &SuperTraceProvider{
PrestateProvider: prestateProvider,
logger: logger,
rootProvider: rootProvider,
prestateTimestamp: prestateTimestamp,
poststateTimestamp: poststateTimestamp,
l1Head: l1Head,
gameDepth: gameDepth,
PreimagePrestateProvider: prestateProvider,
logger: logger,
rootProvider: rootProvider,
prestateTimestamp: prestateTimestamp,
poststateTimestamp: poststateTimestamp,
l1Head: l1Head,
gameDepth: gameDepth,
}
}
func (s *SuperTraceProvider) Get(ctx context.Context, pos types.Position) (common.Hash, error) {
preimage, err := s.GetPreimageBytes(ctx, pos)
if err != nil {
return common.Hash{}, err
}
return crypto.Keccak256Hash(preimage), nil
}
func (s *SuperTraceProvider) GetPreimageBytes(ctx context.Context, pos types.Position) ([]byte, error) {
// Find the timestamp and step at position
timestamp, step, err := s.ComputeStep(pos)
if err != nil {
return common.Hash{}, err
return nil, err
}
s.logger.Info("Getting claim", "pos", pos.ToGIndex(), "timestamp", timestamp, "step", step)
if step == 0 {
root, err := s.rootProvider.SuperRootAtTimestamp(timestamp)
root, err := s.rootProvider.SuperRootAtTimestamp(ctx, timestamp)
if err != nil {
return common.Hash{}, fmt.Errorf("failed to retrieve super root at timestamp %v: %w", timestamp, err)
return nil, fmt.Errorf("failed to retrieve super root at timestamp %v: %w", timestamp, err)
}
return common.Hash(root.SuperRoot), nil
return responseToSuper(root).Marshal(), nil
}
// Fetch the super root at the next timestamp since we are part way through the transition to it
prevRoot, err := s.rootProvider.SuperRootAtTimestamp(timestamp)
prevRoot, err := s.rootProvider.SuperRootAtTimestamp(ctx, timestamp)
if err != nil {
return common.Hash{}, fmt.Errorf("failed to retrieve super root at timestamp %v: %w", timestamp, err)
return nil, fmt.Errorf("failed to retrieve super root at timestamp %v: %w", timestamp, err)
}
nextTimestamp := timestamp + 1
nextRoot, err := s.rootProvider.SuperRootAtTimestamp(nextTimestamp)
nextRoot, err := s.rootProvider.SuperRootAtTimestamp(ctx, nextTimestamp)
if err != nil {
return common.Hash{}, fmt.Errorf("failed to retrieve super root at timestamp %v: %w", nextTimestamp, err)
}
prevChainOutputs := make([]eth.ChainIDAndOutput, 0, len(prevRoot.Chains))
for _, chain := range prevRoot.Chains {
prevChainOutputs = append(prevChainOutputs, eth.ChainIDAndOutput{ChainID: chain.ChainID.ToBig().Uint64(), Output: chain.Canonical})
return nil, fmt.Errorf("failed to retrieve super root at timestamp %v: %w", nextTimestamp, err)
}
superV1 := responseToSuper(prevRoot)
expectedState := interopTypes.TransitionState{
SuperRoot: eth.NewSuperV1(prevRoot.Timestamp, prevChainOutputs...).Marshal(),
SuperRoot: superV1.Marshal(),
PendingProgress: make([]interopTypes.OptimisticBlock, 0, step),
Step: step,
}
for i := uint64(0); i < min(step, uint64(len(prevChainOutputs))); i++ {
for i := uint64(0); i < min(step, uint64(len(nextRoot.Chains))); i++ {
rawOutput, err := eth.UnmarshalOutput(nextRoot.Chains[i].Pending)
if err != nil {
return common.Hash{}, fmt.Errorf("failed to unmarshal pending output %v at timestamp %v: %w", i, nextTimestamp, err)
return nil, fmt.Errorf("failed to unmarshal pending output %v at timestamp %v: %w", i, nextTimestamp, err)
}
output, ok := rawOutput.(*eth.OutputV0)
if !ok {
return common.Hash{}, fmt.Errorf("unsupported output version %v at timestamp %v", output.Version(), nextTimestamp)
return nil, fmt.Errorf("unsupported output version %v at timestamp %v", output.Version(), nextTimestamp)
}
expectedState.PendingProgress = append(expectedState.PendingProgress, interopTypes.OptimisticBlock{
BlockHash: output.BlockHash,
OutputRoot: eth.OutputRoot(output),
})
}
return expectedState.Hash(), nil
return expectedState.Marshal(), nil
}
func (s *SuperTraceProvider) ComputeStep(pos types.Position) (timestamp uint64, step uint64, err error) {
......
......@@ -26,10 +26,9 @@ var (
func TestGet(t *testing.T) {
t.Run("AtPostState", func(t *testing.T) {
provider, stubSupervisor := createProvider(t)
superRoot := eth.Bytes32{0xaa}
stubSupervisor.Add(eth.SuperRootResponse{
response := eth.SuperRootResponse{
Timestamp: poststateTimestamp,
SuperRoot: superRoot,
SuperRoot: eth.Bytes32{0xaa},
Chains: []eth.ChainRootInfo{
{
ChainID: eth.ChainIDFromUInt64(1),
......@@ -37,18 +36,19 @@ func TestGet(t *testing.T) {
Pending: []byte{0xcc},
},
},
})
}
stubSupervisor.Add(response)
claim, err := provider.Get(context.Background(), types.RootPosition)
require.NoError(t, err)
require.Equal(t, common.Hash(superRoot), claim)
expected := responseToSuper(response)
require.Equal(t, common.Hash(eth.SuperRoot(expected)), claim)
})
t.Run("AtNewTimestamp", func(t *testing.T) {
provider, stubSupervisor := createProvider(t)
superRoot := eth.Bytes32{0xaa}
stubSupervisor.Add(eth.SuperRootResponse{
response := eth.SuperRootResponse{
Timestamp: prestateTimestamp + 1,
SuperRoot: superRoot,
SuperRoot: eth.Bytes32{0xaa},
Chains: []eth.ChainRootInfo{
{
ChainID: eth.ChainIDFromUInt64(1),
......@@ -56,10 +56,12 @@ func TestGet(t *testing.T) {
Pending: []byte{0xcc},
},
},
})
}
stubSupervisor.Add(response)
claim, err := provider.Get(context.Background(), types.NewPosition(gameDepth, big.NewInt(StepsPerTimestamp-1)))
require.NoError(t, err)
require.Equal(t, common.Hash(superRoot), claim)
expected := responseToSuper(response)
require.Equal(t, common.Hash(eth.SuperRoot(expected)), claim)
})
t.Run("FirstTimestamp", func(t *testing.T) {
......@@ -240,7 +242,7 @@ func (s *stubRootProvider) Add(root eth.SuperRootResponse) {
s.rootsByTimestamp[root.Timestamp] = root
}
func (s *stubRootProvider) SuperRootAtTimestamp(timestamp uint64) (eth.SuperRootResponse, error) {
func (s *stubRootProvider) SuperRootAtTimestamp(_ context.Context, timestamp uint64) (eth.SuperRootResponse, error) {
root, ok := s.rootsByTimestamp[timestamp]
if !ok {
return eth.SuperRootResponse{}, ethereum.NotFound
......
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