Commit 5f4757cb authored by Adrian Sutton's avatar Adrian Sutton Committed by GitHub

op-challenger: Hook up the super game type (#13794)

* op-challenger: Implement split adapter and trace accessor creation for super cannon

* op-challenger: Implement register task for super cannon

* op-challenger: Actually register super cannon game handler

* op-challenger: Lazy create clients

Make sync validator configurable per game type.

* op-challenger: Test split adapter

Remove ClaimTimestamp from ClaimInfo as we always use the poststate timestamp
parent 1820f29c
...@@ -5,8 +5,11 @@ import ( ...@@ -5,8 +5,11 @@ import (
"fmt" "fmt"
"github.com/ethereum-optimism/optimism/op-challenger/config" "github.com/ethereum-optimism/optimism/op-challenger/config"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace/super"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace/utils" "github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace/utils"
"github.com/ethereum-optimism/optimism/op-service/client"
"github.com/ethereum-optimism/optimism/op-service/dial" "github.com/ethereum-optimism/optimism/op-service/dial"
"github.com/ethereum-optimism/optimism/op-service/sources"
"github.com/ethereum/go-ethereum/ethclient" "github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
) )
...@@ -18,6 +21,7 @@ type clientProvider struct { ...@@ -18,6 +21,7 @@ type clientProvider struct {
l2HeaderSource utils.L2HeaderSource l2HeaderSource utils.L2HeaderSource
rollupClient RollupClient rollupClient RollupClient
syncValidator *syncStatusValidator syncValidator *syncStatusValidator
rootProvider super.RootProvider
toClose []CloseFunc toClose []CloseFunc
} }
...@@ -66,3 +70,14 @@ func (c *clientProvider) RollupClient() (RollupClient, error) { ...@@ -66,3 +70,14 @@ func (c *clientProvider) RollupClient() (RollupClient, error) {
c.toClose = append(c.toClose, rollupClient.Close) c.toClose = append(c.toClose, rollupClient.Close)
return rollupClient, nil return rollupClient, nil
} }
func (c *clientProvider) SuperRootProvider() (super.RootProvider, error) {
cl, err := client.NewRPC(context.Background(), c.logger, c.cfg.SupervisorRPC)
if err != nil {
return nil, fmt.Errorf("failed to dial supervisor: %w", err)
}
supervisorClient := sources.NewSupervisorClient(cl)
c.rootProvider = supervisorClient
c.toClose = append(c.toClose, supervisorClient.Close)
return supervisorClient, nil
}
...@@ -67,6 +67,13 @@ func RegisterGameTypes( ...@@ -67,6 +67,13 @@ func RegisterGameTypes(
} }
registerTasks = append(registerTasks, NewCannonRegisterTask(faultTypes.CannonGameType, cfg, m, vm.NewOpProgramServerExecutor(logger), l2HeaderSource, rollupClient, syncValidator)) registerTasks = append(registerTasks, NewCannonRegisterTask(faultTypes.CannonGameType, cfg, m, vm.NewOpProgramServerExecutor(logger), l2HeaderSource, rollupClient, syncValidator))
} }
if cfg.TraceTypeEnabled(faultTypes.TraceTypeSuperCannon) {
rootProvider, err := clients.SuperRootProvider()
if err != nil {
return nil, err
}
registerTasks = append(registerTasks, NewSuperCannonRegisterTask(faultTypes.SuperCannonGameType, cfg, m, vm.NewOpProgramServerExecutor(logger), rootProvider))
}
if cfg.TraceTypeEnabled(faultTypes.TraceTypePermissioned) { if cfg.TraceTypeEnabled(faultTypes.TraceTypePermissioned) {
l2HeaderSource, rollupClient, syncValidator, err := clients.SingleChainClients() l2HeaderSource, rollupClient, syncValidator, err := clients.SingleChainClients()
if err != nil { if err != nil {
......
...@@ -15,6 +15,7 @@ import ( ...@@ -15,6 +15,7 @@ import (
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace/cannon" "github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace/cannon"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace/outputs" "github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace/outputs"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace/prestates" "github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace/prestates"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace/super"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace/utils" "github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace/utils"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace/vm" "github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace/vm"
faultTypes "github.com/ethereum-optimism/optimism/op-challenger/game/fault/types" faultTypes "github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
...@@ -49,6 +50,41 @@ type RegisterTask struct { ...@@ -49,6 +50,41 @@ type RegisterTask struct {
poststateBlock uint64) (*trace.Accessor, error) poststateBlock uint64) (*trace.Accessor, error)
} }
func NewSuperCannonRegisterTask(gameType faultTypes.GameType, cfg *config.Config, m caching.Metrics, serverExecutor vm.OracleServerExecutor, rootProvider super.RootProvider) *RegisterTask {
stateConverter := cannon.NewStateConverter(cfg.Cannon)
return &RegisterTask{
gameType: gameType,
syncValidator: super.NewSyncValidator(),
getTopPrestateProvider: func(ctx context.Context, prestateTimestamp uint64) (faultTypes.PrestateProvider, error) {
return super.NewSuperRootPrestateProvider(rootProvider, prestateTimestamp), nil
},
getBottomPrestateProvider: cachePrestates(
gameType,
stateConverter,
m,
cfg.CannonAbsolutePreStateBaseURL,
cfg.CannonAbsolutePreState,
filepath.Join(cfg.Datadir, "super-cannon-prestates"),
func(ctx context.Context, path string) faultTypes.PrestateProvider {
return vm.NewPrestateProvider(path, stateConverter)
}),
newTraceAccessor: func(
logger log.Logger,
m metrics.Metricer,
prestateProvider faultTypes.PrestateProvider,
vmPrestateProvider faultTypes.PrestateProvider,
dir string,
l1Head eth.BlockID,
splitDepth faultTypes.Depth,
prestateBlock uint64,
poststateBlock uint64) (*trace.Accessor, error) {
provider := vmPrestateProvider.(*vm.PrestateProvider)
preimagePrestateProvider := prestateProvider.(super.PreimagePrestateProvider)
return super.NewSuperCannonTraceAccessor(logger, m, cfg.Cannon, serverExecutor, preimagePrestateProvider, rootProvider, provider.PrestatePath(), dir, l1Head, splitDepth, prestateBlock, poststateBlock)
},
}
}
func NewCannonRegisterTask(gameType faultTypes.GameType, cfg *config.Config, m caching.Metrics, serverExecutor vm.OracleServerExecutor, l2Client utils.L2HeaderSource, rollupClient outputs.OutputRollupClient, syncValidator SyncValidator) *RegisterTask { func NewCannonRegisterTask(gameType faultTypes.GameType, cfg *config.Config, m caching.Metrics, serverExecutor vm.OracleServerExecutor, l2Client utils.L2HeaderSource, rollupClient outputs.OutputRollupClient, syncValidator SyncValidator) *RegisterTask {
stateConverter := cannon.NewStateConverter(cfg.Cannon) stateConverter := cannon.NewStateConverter(cfg.Cannon)
return &RegisterTask{ return &RegisterTask{
......
...@@ -13,7 +13,6 @@ import ( ...@@ -13,7 +13,6 @@ import (
"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/crypto"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
...@@ -157,64 +156,6 @@ func (c *capturingCreator) Create(_ context.Context, localContext common.Hash, _ ...@@ -157,64 +156,6 @@ func (c *capturingCreator) Create(_ context.Context, localContext common.Hash, _
return nil, creatorError return nil, creatorError
} }
func TestCreateLocalContext(t *testing.T) {
tests := []struct {
name string
preValue common.Hash
prePosition types.Position
postValue common.Hash
postPosition types.Position
expected []byte
}{
{
name: "PreAndPost",
preValue: common.HexToHash("abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789"),
prePosition: types.NewPositionFromGIndex(big.NewInt(2)),
postValue: common.HexToHash("cc00000000000000000000000000000000000000000000000000000000000000"),
postPosition: types.NewPositionFromGIndex(big.NewInt(3)),
expected: common.FromHex("abcdef0123456789abcdef0123456789abcdef0123456789abcdef01234567890000000000000000000000000000000000000000000000000000000000000002cc000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003"),
},
{
name: "LargePositions",
preValue: common.HexToHash("abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789"),
prePosition: types.NewPositionFromGIndex(new(big.Int).SetBytes(common.FromHex("cbcdef0123456789abcdef0123456789abcdef0123456789abcdef012345678c"))),
postValue: common.HexToHash("dd00000000000000000000000000000000000000000000000000000000000000"),
postPosition: types.NewPositionFromGIndex(new(big.Int).SetUint64(math.MaxUint64)),
expected: common.FromHex("abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789cbcdef0123456789abcdef0123456789abcdef0123456789abcdef012345678cdd00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffff"),
},
{
name: "AbsolutePreState",
preValue: common.Hash{},
prePosition: types.Position{},
postValue: common.HexToHash("cc00000000000000000000000000000000000000000000000000000000000000"),
postPosition: types.NewPositionFromGIndex(big.NewInt(3)),
expected: common.FromHex("cc000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003"),
},
}
for _, test := range tests {
test := test
t.Run(test.name, func(t *testing.T) {
pre := types.Claim{
ClaimData: types.ClaimData{
Value: test.preValue,
Position: test.prePosition,
},
}
post := types.Claim{
ClaimData: types.ClaimData{
Value: test.postValue,
Position: test.postPosition,
},
}
actualPreimage := split.LocalContextPreimage(pre, post)
require.Equal(t, test.expected, actualPreimage)
localContext := split.CreateLocalContext(pre, post)
require.Equal(t, crypto.Keccak256Hash(test.expected), localContext)
})
}
}
type stubPrestateProvider struct { type stubPrestateProvider struct {
errorsOnAbsolutePrestateFetch bool errorsOnAbsolutePrestateFetch bool
absolutePrestate common.Hash absolutePrestate common.Hash
......
package split
import (
"math"
"math/big"
"testing"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/stretchr/testify/require"
)
func TestCreateLocalContext(t *testing.T) {
tests := []struct {
name string
preValue common.Hash
prePosition types.Position
postValue common.Hash
postPosition types.Position
expected []byte
}{
{
name: "PreAndPost",
preValue: common.HexToHash("abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789"),
prePosition: types.NewPositionFromGIndex(big.NewInt(2)),
postValue: common.HexToHash("cc00000000000000000000000000000000000000000000000000000000000000"),
postPosition: types.NewPositionFromGIndex(big.NewInt(3)),
expected: common.FromHex("abcdef0123456789abcdef0123456789abcdef0123456789abcdef01234567890000000000000000000000000000000000000000000000000000000000000002cc000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003"),
},
{
name: "LargePositions",
preValue: common.HexToHash("abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789"),
prePosition: types.NewPositionFromGIndex(new(big.Int).SetBytes(common.FromHex("cbcdef0123456789abcdef0123456789abcdef0123456789abcdef012345678c"))),
postValue: common.HexToHash("dd00000000000000000000000000000000000000000000000000000000000000"),
postPosition: types.NewPositionFromGIndex(new(big.Int).SetUint64(math.MaxUint64)),
expected: common.FromHex("abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789cbcdef0123456789abcdef0123456789abcdef0123456789abcdef012345678cdd00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffff"),
},
{
name: "AbsolutePreState",
preValue: common.Hash{},
prePosition: types.Position{},
postValue: common.HexToHash("cc00000000000000000000000000000000000000000000000000000000000000"),
postPosition: types.NewPositionFromGIndex(big.NewInt(3)),
expected: common.FromHex("cc000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003"),
},
}
for _, test := range tests {
test := test
t.Run(test.name, func(t *testing.T) {
pre := types.Claim{
ClaimData: types.ClaimData{
Value: test.preValue,
Position: test.prePosition,
},
}
post := types.Claim{
ClaimData: types.ClaimData{
Value: test.postValue,
Position: test.postPosition,
},
}
actualPreimage := LocalContextPreimage(pre, post)
require.Equal(t, test.expected, actualPreimage)
localContext := CreateLocalContext(pre, post)
require.Equal(t, crypto.Keccak256Hash(test.expected), localContext)
})
}
}
...@@ -5,6 +5,7 @@ import ( ...@@ -5,6 +5,7 @@ import (
"github.com/ethereum-optimism/optimism/op-service/eth" "github.com/ethereum-optimism/optimism/op-service/eth"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
) )
type SuperRootPrestateProvider struct { type SuperRootPrestateProvider struct {
...@@ -30,7 +31,7 @@ func (s *SuperRootPrestateProvider) AbsolutePreStateCommitment(ctx context.Conte ...@@ -30,7 +31,7 @@ func (s *SuperRootPrestateProvider) AbsolutePreStateCommitment(ctx context.Conte
} }
func (s *SuperRootPrestateProvider) AbsolutePreState(ctx context.Context) (eth.Super, error) { func (s *SuperRootPrestateProvider) AbsolutePreState(ctx context.Context) (eth.Super, error) {
response, err := s.provider.SuperRootAtTimestamp(ctx, s.timestamp) response, err := s.provider.SuperRootAtTimestamp(ctx, hexutil.Uint64(s.timestamp))
if err != nil { if err != nil {
return nil, err return nil, err
} }
......
...@@ -9,6 +9,7 @@ import ( ...@@ -9,6 +9,7 @@ import (
interopTypes "github.com/ethereum-optimism/optimism/op-program/client/interop/types" interopTypes "github.com/ethereum-optimism/optimism/op-program/client/interop/types"
"github.com/ethereum-optimism/optimism/op-service/eth" "github.com/ethereum-optimism/optimism/op-service/eth"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
) )
...@@ -30,7 +31,7 @@ type PreimagePrestateProvider interface { ...@@ -30,7 +31,7 @@ type PreimagePrestateProvider interface {
AbsolutePreState(ctx context.Context) (eth.Super, error) AbsolutePreState(ctx context.Context) (eth.Super, error)
} }
type RootProvider interface { type RootProvider interface {
SuperRootAtTimestamp(ctx context.Context, timestamp uint64) (eth.SuperRootResponse, error) SuperRootAtTimestamp(ctx context.Context, timestamp hexutil.Uint64) (eth.SuperRootResponse, error)
} }
type SuperTraceProvider struct { type SuperTraceProvider struct {
...@@ -71,19 +72,19 @@ func (s *SuperTraceProvider) GetPreimageBytes(ctx context.Context, pos types.Pos ...@@ -71,19 +72,19 @@ func (s *SuperTraceProvider) GetPreimageBytes(ctx context.Context, pos types.Pos
} }
s.logger.Info("Getting claim", "pos", pos.ToGIndex(), "timestamp", timestamp, "step", step) s.logger.Info("Getting claim", "pos", pos.ToGIndex(), "timestamp", timestamp, "step", step)
if step == 0 { if step == 0 {
root, err := s.rootProvider.SuperRootAtTimestamp(ctx, timestamp) root, err := s.rootProvider.SuperRootAtTimestamp(ctx, hexutil.Uint64(timestamp))
if err != nil { if err != nil {
return nil, 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 responseToSuper(root).Marshal(), nil return responseToSuper(root).Marshal(), nil
} }
// Fetch the super root at the next timestamp since we are part way through the transition to it // Fetch the super root at the next timestamp since we are part way through the transition to it
prevRoot, err := s.rootProvider.SuperRootAtTimestamp(ctx, timestamp) prevRoot, err := s.rootProvider.SuperRootAtTimestamp(ctx, hexutil.Uint64(timestamp))
if err != nil { if err != nil {
return nil, 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 nextTimestamp := timestamp + 1
nextRoot, err := s.rootProvider.SuperRootAtTimestamp(ctx, nextTimestamp) nextRoot, err := s.rootProvider.SuperRootAtTimestamp(ctx, hexutil.Uint64(nextTimestamp))
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to retrieve super root at timestamp %v: %w", nextTimestamp, err) return nil, fmt.Errorf("failed to retrieve super root at timestamp %v: %w", nextTimestamp, err)
} }
......
package super
import (
"context"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
"github.com/ethereum-optimism/optimism/op-service/sources/caching"
"github.com/ethereum/go-ethereum/common"
)
type ProviderCache struct {
cache *caching.LRUCache[common.Hash, types.TraceProvider]
creator ProposalTraceProviderCreator
}
func (c *ProviderCache) GetOrCreate(ctx context.Context, localContext common.Hash, depth types.Depth, claimInfo ClaimInfo) (types.TraceProvider, error) {
provider, ok := c.cache.Get(localContext)
if ok {
return provider, nil
}
provider, err := c.creator(ctx, localContext, depth, claimInfo)
if err != nil {
return nil, err
}
c.cache.Add(localContext, provider)
return provider, nil
}
func NewProviderCache(m caching.Metrics, metricsLabel string, creator ProposalTraceProviderCreator) *ProviderCache {
cache := caching.NewLRUCache[common.Hash, types.TraceProvider](m, metricsLabel, 100)
return &ProviderCache{
cache: cache,
creator: creator,
}
}
package super
import (
"context"
"errors"
"math/big"
"testing"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace/alphabet"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
"github.com/ethereum-optimism/optimism/op-challenger/metrics"
"github.com/ethereum/go-ethereum/common"
"github.com/stretchr/testify/require"
)
func TestProviderCache(t *testing.T) {
claimInfo := ClaimInfo{
AgreedPrestate: []byte{1, 2, 3, 4},
Claim: common.Hash{0xaa, 0x66},
}
depth := types.Depth(6)
var createdProvider types.TraceProvider
creator := func(ctx context.Context, localContext common.Hash, depth types.Depth, claimInfo ClaimInfo) (types.TraceProvider, error) {
createdProvider = alphabet.NewTraceProvider(big.NewInt(0), depth)
return createdProvider, nil
}
localContext1 := common.Hash{0xdd}
localContext2 := common.Hash{0xee}
cache := NewProviderCache(metrics.NoopMetrics, "test", creator)
// Create on first call
provider1, err := cache.GetOrCreate(context.Background(), localContext1, depth, claimInfo)
require.NoError(t, err)
require.Same(t, createdProvider, provider1, "should return created trace provider")
// Return the cached provider on subsequent calls.
createdProvider = nil
cached, err := cache.GetOrCreate(context.Background(), localContext1, depth, claimInfo)
require.NoError(t, err)
require.Same(t, provider1, cached, "should return exactly the same instance from cache")
require.Nil(t, createdProvider)
// Create a new provider when the local context is different
createdProvider = nil
otherProvider, err := cache.GetOrCreate(context.Background(), localContext2, depth, claimInfo)
require.NoError(t, err)
require.Same(t, otherProvider, createdProvider, "should return newly created trace provider")
require.NotSame(t, otherProvider, provider1, "should not use cached provider for different local context")
}
func TestProviderCache_DoNotCacheErrors(t *testing.T) {
callCount := 0
providerErr := errors.New("boom")
creator := func(ctx context.Context, localContext common.Hash, depth types.Depth, claimInfo ClaimInfo) (types.TraceProvider, error) {
callCount++
return nil, providerErr
}
localContext1 := common.Hash{0xdd}
cache := NewProviderCache(metrics.NoopMetrics, "test", creator)
provider, err := cache.GetOrCreate(context.Background(), localContext1, 6, ClaimInfo{})
require.Nil(t, provider)
require.ErrorIs(t, err, providerErr)
require.Equal(t, 1, callCount)
// Should call the creator again on the second attempt
provider, err = cache.GetOrCreate(context.Background(), localContext1, 6, ClaimInfo{})
require.Nil(t, provider)
require.ErrorIs(t, err, providerErr)
require.Equal(t, 2, callCount)
}
...@@ -2,6 +2,7 @@ package super ...@@ -2,6 +2,7 @@ package super
import ( import (
"context" "context"
"fmt"
"math/big" "math/big"
"math/rand" "math/rand"
"testing" "testing"
...@@ -13,6 +14,7 @@ import ( ...@@ -13,6 +14,7 @@ import (
"github.com/ethereum-optimism/optimism/op-service/testutils" "github.com/ethereum-optimism/optimism/op-service/testutils"
"github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
...@@ -242,10 +244,10 @@ func (s *stubRootProvider) Add(root eth.SuperRootResponse) { ...@@ -242,10 +244,10 @@ func (s *stubRootProvider) Add(root eth.SuperRootResponse) {
s.rootsByTimestamp[root.Timestamp] = root s.rootsByTimestamp[root.Timestamp] = root
} }
func (s *stubRootProvider) SuperRootAtTimestamp(_ context.Context, timestamp uint64) (eth.SuperRootResponse, error) { func (s *stubRootProvider) SuperRootAtTimestamp(_ context.Context, timestamp hexutil.Uint64) (eth.SuperRootResponse, error) {
root, ok := s.rootsByTimestamp[timestamp] root, ok := s.rootsByTimestamp[uint64(timestamp)]
if !ok { if !ok {
return eth.SuperRootResponse{}, ethereum.NotFound return eth.SuperRootResponse{}, fmt.Errorf("timestamp %v %w", uint64(timestamp), ethereum.NotFound)
} }
return root, nil return root, nil
} }
package super
import (
"context"
"fmt"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace/split"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
"github.com/ethereum-optimism/optimism/op-service/eth"
"github.com/ethereum/go-ethereum/common"
)
type ProposalTraceProviderCreator func(ctx context.Context, localContext common.Hash, depth types.Depth, claimInfo ClaimInfo) (types.TraceProvider, error)
type PreimageTraceProvider interface {
AbsolutePreState(ctx context.Context) (eth.Super, error)
GetPreimageBytes(ctx context.Context, pos types.Position) ([]byte, error)
ComputeStep(pos types.Position) (timestamp uint64, step uint64, err error)
}
func SuperRootSplitAdapter(topProvider PreimageTraceProvider, creator ProposalTraceProviderCreator) split.ProviderCreator {
return func(ctx context.Context, depth types.Depth, pre types.Claim, post types.Claim) (types.TraceProvider, error) {
localContext := split.CreateLocalContext(pre, post)
claimInfo, err := FetchClaimInfo(ctx, topProvider, pre, post)
if err != nil {
return nil, err
}
return creator(ctx, localContext, depth, claimInfo)
}
}
func FetchClaimInfo(ctx context.Context, topProvider PreimageTraceProvider, pre types.Claim, post types.Claim) (ClaimInfo, error) {
usePrestateBlock := pre == (types.Claim{})
var claimInfo ClaimInfo
if usePrestateBlock {
absolutePrestatePreimage, err := topProvider.AbsolutePreState(ctx)
if err != nil {
return ClaimInfo{}, fmt.Errorf("failed to retrieve absolute prestate preimage: %w", err)
}
claimInfo.AgreedPrestate = absolutePrestatePreimage.Marshal()
} else {
agreedPrestate, err := topProvider.GetPreimageBytes(ctx, pre.Position)
if err != nil {
return ClaimInfo{}, fmt.Errorf("failed to get prestate preimage: %w", err)
}
claimInfo.AgreedPrestate = agreedPrestate
}
claimInfo.Claim = post.Value
return claimInfo, nil
}
type ClaimInfo struct {
AgreedPrestate []byte
Claim common.Hash
}
package super
import (
"context"
"errors"
"math/big"
"testing"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace/split"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
interopTypes "github.com/ethereum-optimism/optimism/op-program/client/interop/types"
"github.com/ethereum-optimism/optimism/op-service/eth"
"github.com/ethereum-optimism/optimism/op-service/testlog"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/log"
"github.com/stretchr/testify/require"
)
var creatorError = errors.New("captured args")
func TestSplitAdapter(t *testing.T) {
depth := types.Depth(30)
prestateTimestamp := uint64(150)
poststateTimestamp := uint64(200)
t.Run("FromAbsolutePrestate", func(t *testing.T) {
creator, rootProvider, adapter := setupSplitAdapterTest(t, depth, prestateTimestamp, poststateTimestamp)
prestateResponse := eth.SuperRootResponse{
Timestamp: prestateTimestamp,
}
rootProvider.Add(prestateResponse)
postClaim := types.Claim{
ClaimData: types.ClaimData{
Value: common.Hash{0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff},
Position: types.NewPosition(depth, big.NewInt(0)),
},
}
expectedClaimInfo := ClaimInfo{
AgreedPrestate: responseToSuper(prestateResponse).Marshal(),
Claim: postClaim.Value,
}
_, err := adapter(context.Background(), depth, types.Claim{}, postClaim)
require.ErrorIs(t, err, creatorError)
require.Equal(t, split.CreateLocalContext(types.Claim{}, postClaim), creator.localContext)
require.Equal(t, expectedClaimInfo, creator.claimInfo)
})
t.Run("AfterClaimedBlock", func(t *testing.T) {
creator, rootProvider, adapter := setupSplitAdapterTest(t, depth, prestateTimestamp, poststateTimestamp)
prestateResponse := eth.SuperRootResponse{
Timestamp: poststateTimestamp,
}
rootProvider.Add(prestateResponse)
preClaim := types.Claim{
ClaimData: types.ClaimData{
Value: common.Hash{0x11},
Position: types.NewPosition(depth, big.NewInt(999_999)),
},
}
postClaim := types.Claim{
ClaimData: types.ClaimData{
Value: common.Hash{0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff},
Position: types.NewPosition(depth, big.NewInt(1_000_000)),
},
}
expectedClaimInfo := ClaimInfo{
AgreedPrestate: responseToSuper(prestateResponse).Marshal(),
Claim: postClaim.Value,
}
_, err := adapter(context.Background(), depth, preClaim, postClaim)
require.ErrorIs(t, err, creatorError)
require.Equal(t, split.CreateLocalContext(preClaim, postClaim), creator.localContext)
require.Equal(t, expectedClaimInfo, creator.claimInfo)
})
t.Run("MiddleOfTimestampTransition", func(t *testing.T) {
creator, rootProvider, adapter := setupSplitAdapterTest(t, depth, prestateTimestamp, poststateTimestamp)
prestateResponse := eth.SuperRootResponse{
Timestamp: prestateTimestamp,
}
rootProvider.Add(prestateResponse)
rootProvider.Add(eth.SuperRootResponse{
Timestamp: prestateTimestamp + 1,
})
preClaim := types.Claim{
ClaimData: types.ClaimData{
Value: common.Hash{0x11},
Position: types.NewPosition(depth, big.NewInt(2)),
},
}
postClaim := types.Claim{
ClaimData: types.ClaimData{
Value: common.Hash{0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff},
Position: types.NewPosition(depth, big.NewInt(3)),
},
}
expectedPrestate := interopTypes.TransitionState{
SuperRoot: responseToSuper(prestateResponse).Marshal(),
Step: 3,
}
expectedClaimInfo := ClaimInfo{
AgreedPrestate: expectedPrestate.Marshal(),
Claim: postClaim.Value,
}
_, err := adapter(context.Background(), depth, preClaim, postClaim)
require.ErrorIs(t, err, creatorError)
require.Equal(t, split.CreateLocalContext(preClaim, postClaim), creator.localContext)
require.Equal(t, expectedClaimInfo, creator.claimInfo)
})
}
func setupSplitAdapterTest(t *testing.T, depth types.Depth, prestateTimestamp uint64, poststateTimestamp uint64) (*capturingCreator, *stubRootProvider, split.ProviderCreator) {
creator := &capturingCreator{}
rootProvider := &stubRootProvider{}
prestateProvider := NewSuperRootPrestateProvider(rootProvider, prestateTimestamp)
traceProvider := NewSuperTraceProvider(testlog.Logger(t, log.LvlInfo), prestateProvider, rootProvider, eth.BlockID{}, depth, prestateTimestamp, poststateTimestamp)
adapter := SuperRootSplitAdapter(traceProvider, creator.Create)
return creator, rootProvider, adapter
}
type capturingCreator struct {
localContext common.Hash
claimInfo ClaimInfo
}
func (c *capturingCreator) Create(_ context.Context, localContext common.Hash, _ types.Depth, claimInfo ClaimInfo) (types.TraceProvider, error) {
c.localContext = localContext
c.claimInfo = claimInfo
return nil, creatorError
}
package super
import (
"context"
"math/big"
"path/filepath"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace/cannon"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace/split"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace/utils"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace/vm"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
"github.com/ethereum-optimism/optimism/op-challenger/metrics"
"github.com/ethereum-optimism/optimism/op-service/eth"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/log"
)
func NewSuperCannonTraceAccessor(
logger log.Logger,
m metrics.Metricer,
cfg vm.Config,
serverExecutor vm.OracleServerExecutor,
prestateProvider PreimagePrestateProvider,
rootProvider RootProvider,
cannonPrestate string,
dir string,
l1Head eth.BlockID,
splitDepth types.Depth,
prestateBlock uint64,
poststateBlock uint64,
) (*trace.Accessor, error) {
outputProvider := NewSuperTraceProvider(logger, prestateProvider, rootProvider, l1Head, splitDepth, prestateBlock, poststateBlock)
cannonCreator := func(ctx context.Context, localContext common.Hash, depth types.Depth, claimInfo ClaimInfo) (types.TraceProvider, error) {
logger := logger.New("agreedPrestate", claimInfo.AgreedPrestate, "claim", claimInfo.Claim, "localContext", localContext)
subdir := filepath.Join(dir, localContext.Hex())
localInputs := utils.LocalGameInputs{
L1Head: l1Head.Hash,
L2OutputRoot: crypto.Keccak256Hash(claimInfo.AgreedPrestate),
L2Claim: claimInfo.Claim,
L2BlockNumber: new(big.Int).SetUint64(poststateBlock),
}
provider := cannon.NewTraceProvider(logger, m.ToTypedVmMetrics(cfg.VmType.String()), cfg, serverExecutor, prestateProvider, cannonPrestate, localInputs, subdir, depth)
return provider, nil
}
cache := NewProviderCache(m, "super_cannon_provider", cannonCreator)
selector := split.NewSplitProviderSelector(outputProvider, splitDepth, SuperRootSplitAdapter(outputProvider, cache.GetOrCreate))
return trace.NewAccessor(selector), nil
}
package super
import (
"context"
"github.com/ethereum-optimism/optimism/op-service/eth"
)
type SyncValidator struct {
}
func NewSyncValidator() *SyncValidator {
return &SyncValidator{}
}
func (s SyncValidator) ValidateNodeSynced(ctx context.Context, gameL1Head eth.BlockID) error {
// TODO: Check sync status of supervisor
return 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