Skip to content

Commit 13e3a7c

Browse files
authored
op-challenger: Make client provider a service level concept (ethereum-optimism#18440)
Allows clients to be shared between fault and zk game types.
1 parent 78350ca commit 13e3a7c

13 files changed

Lines changed: 213 additions & 201 deletions

File tree

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
package client
2+
3+
import (
4+
"context"
5+
"errors"
6+
"fmt"
7+
8+
"github.com/ethereum-optimism/optimism/op-challenger/config"
9+
"github.com/ethereum-optimism/optimism/op-service/dial"
10+
"github.com/ethereum-optimism/optimism/op-service/sources"
11+
"github.com/ethereum-optimism/optimism/op-service/sources/batching"
12+
"github.com/ethereum/go-ethereum/ethclient"
13+
"github.com/ethereum/go-ethereum/log"
14+
)
15+
16+
var ErrNotInSync = errors.New("local node too far behind")
17+
18+
type Provider struct {
19+
ctx context.Context
20+
logger log.Logger
21+
cfg *config.Config
22+
l1Client *ethclient.Client
23+
caller *batching.MultiCaller
24+
25+
l2EL *ethclient.Client
26+
rollupClient *sources.RollupClient
27+
syncValidator *RollupSyncStatusValidator
28+
supervisorClient *sources.SupervisorClient
29+
toClose []func()
30+
}
31+
32+
func NewProvider(ctx context.Context, logger log.Logger, cfg *config.Config, l1Client *ethclient.Client) *Provider {
33+
return &Provider{
34+
ctx: ctx,
35+
logger: logger,
36+
cfg: cfg,
37+
l1Client: l1Client,
38+
caller: batching.NewMultiCaller(l1Client.Client(), batching.DefaultBatchSize),
39+
}
40+
}
41+
42+
func (c *Provider) Close() {
43+
for _, closeFunc := range c.toClose {
44+
closeFunc()
45+
}
46+
}
47+
48+
func (c *Provider) L1Client() *ethclient.Client {
49+
return c.l1Client
50+
}
51+
52+
func (c *Provider) MultiCaller() *batching.MultiCaller {
53+
return c.caller
54+
}
55+
56+
func (c *Provider) SingleChainClients() (*ethclient.Client, *sources.RollupClient, *RollupSyncStatusValidator, error) {
57+
headers, err := c.L2HeaderSource()
58+
if err != nil {
59+
return nil, nil, nil, err
60+
}
61+
rollup, syncValidator, err := c.RollupClients()
62+
if err != nil {
63+
return nil, nil, nil, err
64+
}
65+
return headers, rollup, syncValidator, nil
66+
}
67+
68+
func (c *Provider) L2HeaderSource() (*ethclient.Client, error) {
69+
if c.l2EL != nil {
70+
return c.l2EL, nil
71+
}
72+
if len(c.cfg.L2Rpcs) != 1 {
73+
return nil, fmt.Errorf("incorrect number of L2 RPCs configured, expected 1 but got %d", len(c.cfg.L2Rpcs))
74+
}
75+
76+
l2Client, err := ethclient.DialContext(c.ctx, c.cfg.L2Rpcs[0])
77+
if err != nil {
78+
return nil, fmt.Errorf("dial l2 client %v: %w", c.cfg.L2Rpcs[0], err)
79+
}
80+
c.l2EL = l2Client
81+
c.toClose = append(c.toClose, l2Client.Close)
82+
return l2Client, nil
83+
}
84+
85+
func (c *Provider) RollupClients() (*sources.RollupClient, *RollupSyncStatusValidator, error) {
86+
if c.rollupClient != nil {
87+
return c.rollupClient, c.syncValidator, nil
88+
}
89+
rollupClient, err := dial.DialRollupClientWithTimeout(c.ctx, c.logger, c.cfg.RollupRpc)
90+
if err != nil {
91+
return nil, nil, fmt.Errorf("dial rollup client %v: %w", c.cfg.RollupRpc, err)
92+
}
93+
c.rollupClient = rollupClient
94+
c.syncValidator = NewRollupSyncStatusValidator(rollupClient)
95+
c.toClose = append(c.toClose, rollupClient.Close)
96+
return rollupClient, c.syncValidator, nil
97+
}
98+
99+
func (c *Provider) SuperchainClients() (*sources.SupervisorClient, *SupervisorSyncValidator, error) {
100+
supervisorClient, err := dial.DialSupervisorClientWithTimeout(c.ctx, c.logger, c.cfg.SupervisorRPC)
101+
if err != nil {
102+
return nil, nil, fmt.Errorf("failed to dial supervisor: %w", err)
103+
}
104+
c.supervisorClient = supervisorClient
105+
c.toClose = append(c.toClose, supervisorClient.Close)
106+
return supervisorClient, NewSupervisorSyncValidator(supervisorClient), nil
107+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package client
2+
3+
import (
4+
"context"
5+
"fmt"
6+
7+
"github.com/ethereum-optimism/optimism/op-service/eth"
8+
)
9+
10+
type syncStatusProvider interface {
11+
SyncStatus(context.Context) (*eth.SyncStatus, error)
12+
}
13+
14+
type RollupSyncStatusValidator struct {
15+
statusProvider syncStatusProvider
16+
}
17+
18+
func NewRollupSyncStatusValidator(statusProvider syncStatusProvider) *RollupSyncStatusValidator {
19+
return &RollupSyncStatusValidator{
20+
statusProvider: statusProvider,
21+
}
22+
}
23+
24+
func (s *RollupSyncStatusValidator) ValidateNodeSynced(ctx context.Context, gameL1Head eth.BlockID) error {
25+
syncStatus, err := s.statusProvider.SyncStatus(ctx)
26+
if err != nil {
27+
return fmt.Errorf("failed to retrieve local node sync status: %w", err)
28+
}
29+
if syncStatus.CurrentL1.Number <= gameL1Head.Number {
30+
return fmt.Errorf("%w require L1 block above %v but at %v", ErrNotInSync, gameL1Head.Number, syncStatus.CurrentL1.Number)
31+
}
32+
return nil
33+
}

op-challenger/game/fault/sync_test.go renamed to op-challenger/game/client/rollup_sync_test.go

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
1-
package fault
1+
package client
22

33
import (
44
"context"
55
"errors"
66
"testing"
77

8-
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
98
"github.com/ethereum-optimism/optimism/op-service/eth"
109
"github.com/stretchr/testify/require"
1110
)
@@ -35,7 +34,7 @@ func TestSyncStatusProvider(t *testing.T) {
3534
},
3635
},
3736
statusReqErr: nil,
38-
expected: types.ErrNotInSync,
37+
expected: ErrNotInSync,
3938
},
4039
{
4140
name: "CurrentL1EqualToGameL1Head",
@@ -46,7 +45,7 @@ func TestSyncStatusProvider(t *testing.T) {
4645
},
4746
},
4847
statusReqErr: nil,
49-
expected: types.ErrNotInSync,
48+
expected: ErrNotInSync,
5049
},
5150
{
5251
name: "CurrentL1AboveGameL1Head",
@@ -67,7 +66,7 @@ func TestSyncStatusProvider(t *testing.T) {
6766
status: test.syncStatus,
6867
err: test.statusReqErr,
6968
}
70-
validator := newSyncStatusValidator(provider)
69+
validator := NewRollupSyncStatusValidator(provider)
7170
err := validator.ValidateNodeSynced(context.Background(), test.gameL1Head)
7271
require.ErrorIs(t, err, test.expected)
7372
})
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package client
2+
3+
import (
4+
"context"
5+
"fmt"
6+
7+
"github.com/ethereum-optimism/optimism/op-service/eth"
8+
)
9+
10+
type SupervisorSyncStatusProvider interface {
11+
SyncStatus(ctx context.Context) (eth.SupervisorSyncStatus, error)
12+
}
13+
14+
type SupervisorSyncValidator struct {
15+
syncStatusProvider SupervisorSyncStatusProvider
16+
}
17+
18+
func NewSupervisorSyncValidator(syncStatusProvider SupervisorSyncStatusProvider) *SupervisorSyncValidator {
19+
return &SupervisorSyncValidator{
20+
syncStatusProvider: syncStatusProvider,
21+
}
22+
}
23+
24+
func (s SupervisorSyncValidator) ValidateNodeSynced(ctx context.Context, gameL1Head eth.BlockID) error {
25+
syncStatus, err := s.syncStatusProvider.SyncStatus(ctx)
26+
if err != nil {
27+
return fmt.Errorf("failed to retrieve sync status: %w", err)
28+
}
29+
if syncStatus.MinSyncedL1.Number <= gameL1Head.Number {
30+
return fmt.Errorf("%w require L1 block above %v but at %v", ErrNotInSync, gameL1Head.Number, syncStatus.MinSyncedL1.Number)
31+
}
32+
return nil
33+
}

op-challenger/game/fault/trace/super/sync_test.go renamed to op-challenger/game/client/supervisor_test.go

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,15 @@
1-
package super
1+
package client
22

33
import (
44
"context"
55
"errors"
66
"testing"
77

8-
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
98
"github.com/ethereum-optimism/optimism/op-service/eth"
109
"github.com/stretchr/testify/require"
1110
)
1211

13-
func TestSyncStatusProvider(t *testing.T) {
12+
func TestSupervisorSyncStatusProvider(t *testing.T) {
1413
requestErr := errors.New("boom")
1514
tests := []struct {
1615
name string
@@ -31,15 +30,15 @@ func TestSyncStatusProvider(t *testing.T) {
3130
syncStatus: eth.SupervisorSyncStatus{
3231
MinSyncedL1: eth.L1BlockRef{Number: 99},
3332
},
34-
expectedError: types.ErrNotInSync,
33+
expectedError: ErrNotInSync,
3534
},
3635
{
3736
name: "MinSyncedL1EqualToGameHead",
3837
gameL1Head: 100,
3938
syncStatus: eth.SupervisorSyncStatus{
4039
MinSyncedL1: eth.L1BlockRef{Number: 100},
4140
},
42-
expectedError: types.ErrNotInSync,
41+
expectedError: ErrNotInSync,
4342
},
4443
{
4544
name: "InSync",
@@ -54,22 +53,22 @@ func TestSyncStatusProvider(t *testing.T) {
5453
for _, test := range tests {
5554
test := test // capture range variable
5655
t.Run(test.name, func(t *testing.T) {
57-
stubProvider := &stubSyncStatusProvider{
56+
stubProvider := &stubSupervisorSyncStatusProvider{
5857
status: test.syncStatus,
5958
err: test.statusReqErr,
6059
}
61-
validator := NewSyncValidator(stubProvider)
60+
validator := NewSupervisorSyncValidator(stubProvider)
6261
err := validator.ValidateNodeSynced(context.Background(), eth.BlockID{Number: test.gameL1Head})
6362
require.ErrorIs(t, err, test.expectedError, "expected error to be %v, got %v", test.expectedError, err)
6463
})
6564
}
6665
}
6766

68-
type stubSyncStatusProvider struct {
67+
type stubSupervisorSyncStatusProvider struct {
6968
status eth.SupervisorSyncStatus
7069
err error
7170
}
7271

73-
func (f *stubSyncStatusProvider) SyncStatus(ctx context.Context) (eth.SupervisorSyncStatus, error) {
72+
func (f *stubSupervisorSyncStatusProvider) SyncStatus(ctx context.Context) (eth.SupervisorSyncStatus, error) {
7473
return f.status, f.err
7574
}

op-challenger/game/fault/clients.go

Lines changed: 0 additions & 83 deletions
This file was deleted.

op-challenger/game/fault/player.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"fmt"
77
"time"
88

9+
"github.com/ethereum-optimism/optimism/op-challenger/game/client"
910
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/claims"
1011
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/contracts"
1112
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/preimages"
@@ -183,7 +184,7 @@ func (g *GamePlayer) ProgressGame(ctx context.Context) gameTypes.GameStatus {
183184
g.logger.Trace("Skipping completed game")
184185
return g.status
185186
}
186-
if err := g.syncValidator.ValidateNodeSynced(ctx, g.gameL1Head); errors.Is(err, types.ErrNotInSync) {
187+
if err := g.syncValidator.ValidateNodeSynced(ctx, g.gameL1Head); errors.Is(err, client.ErrNotInSync) {
187188
g.logger.Warn("Local node not sufficiently up to date", "err", err)
188189
return g.status
189190
} else if err != nil {

0 commit comments

Comments
 (0)