Skip to content

Commit e6d2d6c

Browse files
committed
fix(kt-devnet): support multiple depsets per devnet
1 parent 96da6d5 commit e6d2d6c

File tree

8 files changed

+226
-45
lines changed

8 files changed

+226
-45
lines changed

devnet-sdk/descriptors/deployment.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,6 @@ type DevnetEnvironment struct {
7575
L1 *Chain `json:"l1"`
7676
L2 []*L2Chain `json:"l2"`
7777

78-
Features []string `json:"features,omitempty"`
79-
DepSets []DepSet `json:"dep_sets,omitempty"`
78+
Features []string `json:"features,omitempty"`
79+
DepSets map[string]DepSet `json:"dep_sets,omitempty"`
8080
}

kurtosis-devnet/pkg/kurtosis/adapters.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ var _ interfaces.JWTExtractor = (*enclaveJWTAdapter)(nil)
4747

4848
type enclaveDepsetAdapter struct{}
4949

50-
func (a *enclaveDepsetAdapter) ExtractData(ctx context.Context, enclave string) ([]descriptors.DepSet, error) {
50+
func (a *enclaveDepsetAdapter) ExtractData(ctx context.Context, enclave string) (map[string]descriptors.DepSet, error) {
5151
return depset.NewExtractor(enclave).ExtractData(ctx)
5252
}
5353

kurtosis-devnet/pkg/kurtosis/endpoints.go

+64-14
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,29 @@
11
package kurtosis
22

33
import (
4+
"encoding/json"
45
"strconv"
56
"strings"
67

78
"github.com/ethereum-optimism/optimism/devnet-sdk/descriptors"
89
"github.com/ethereum-optimism/optimism/kurtosis-devnet/pkg/kurtosis/sources/inspect"
10+
"github.com/ethereum-optimism/optimism/kurtosis-devnet/pkg/kurtosis/sources/spec"
11+
"github.com/ethereum-optimism/optimism/op-service/eth"
12+
"github.com/ethereum-optimism/optimism/op-supervisor/supervisor/backend/depset"
913
)
1014

15+
type ChainSpec struct {
16+
spec.ChainSpec
17+
DepSets map[string]descriptors.DepSet
18+
}
19+
1120
// ServiceFinder is the main entry point for finding services and their endpoints
1221
type ServiceFinder struct {
1322
services inspect.ServiceMap
1423
nodeServices []string
1524
l2ServicePrefix string
16-
l2Networks []string
25+
l2Networks []ChainSpec
26+
globalServices []string
1727
}
1828

1929
// ServiceFinderOption configures a ServiceFinder
@@ -34,18 +44,26 @@ func WithL2ServicePrefix(prefix string) ServiceFinderOption {
3444
}
3545

3646
// WithL2Networks sets the L2 networks
37-
func WithL2Networks(networks []string) ServiceFinderOption {
47+
func WithL2Networks(networks []ChainSpec) ServiceFinderOption {
3848
return func(f *ServiceFinder) {
3949
f.l2Networks = networks
4050
}
4151
}
4252

53+
// WithGlobalServices sets the global services
54+
func WithGlobalServices(services []string) ServiceFinderOption {
55+
return func(f *ServiceFinder) {
56+
f.globalServices = services
57+
}
58+
}
59+
4360
// NewServiceFinder creates a new ServiceFinder with the given options
4461
func NewServiceFinder(services inspect.ServiceMap, opts ...ServiceFinderOption) *ServiceFinder {
4562
f := &ServiceFinder{
4663
services: services,
4764
nodeServices: []string{"cl", "el"},
4865
l2ServicePrefix: "op-",
66+
globalServices: []string{"op-faucet"},
4967
}
5068
for _, opt := range opts {
5169
opt(f)
@@ -56,11 +74,13 @@ func NewServiceFinder(services inspect.ServiceMap, opts ...ServiceFinderOption)
5674
// FindL1Services finds L1 nodes.
5775
func (f *ServiceFinder) FindL1Services() ([]descriptors.Node, descriptors.ServiceMap) {
5876
return f.findRPCEndpoints(func(serviceName string) (string, int, bool) {
59-
// Only match services that start with one of the node service identifiers.
60-
// We might have to change this if we need to support L1 services beyond nodes.
61-
for _, service := range f.nodeServices {
77+
// Find node services and global services
78+
allServices := append(f.nodeServices, f.globalServices...)
79+
for _, service := range allServices {
6280
if strings.HasPrefix(serviceName, service) {
63-
tag, idx := f.serviceTag(serviceName)
81+
// strip the L2 prefix if it's there.
82+
name := strings.TrimPrefix(serviceName, f.l2ServicePrefix)
83+
tag, idx := f.serviceTag(name)
6484
return tag, idx, true
6585
}
6686
}
@@ -69,24 +89,54 @@ func (f *ServiceFinder) FindL1Services() ([]descriptors.Node, descriptors.Servic
6989
}
7090

7191
// FindL2Services finds L2 nodes and services for a specific network
72-
func (f *ServiceFinder) FindL2Services(network string) ([]descriptors.Node, descriptors.ServiceMap) {
73-
networkSuffix := "-" + network
92+
func (f *ServiceFinder) FindL2Services(s ChainSpec) ([]descriptors.Node, descriptors.ServiceMap) {
93+
network := s.Name
94+
networkID := s.NetworkID
7495
return f.findRPCEndpoints(func(serviceName string) (string, int, bool) {
75-
if strings.HasSuffix(serviceName, networkSuffix) {
76-
name := strings.TrimSuffix(serviceName, networkSuffix)
77-
tag, idx := f.serviceTag(strings.TrimPrefix(name, f.l2ServicePrefix))
78-
return tag, idx, true
96+
possibleSuffixes := []string{"-" + network, "-" + networkID}
97+
for _, suffix := range possibleSuffixes {
98+
if strings.HasSuffix(serviceName, suffix) {
99+
name := strings.TrimSuffix(serviceName, suffix)
100+
tag, idx := f.serviceTag(strings.TrimPrefix(name, f.l2ServicePrefix))
101+
return tag, idx, true
102+
}
79103
}
80104

81105
// skip over the other L2 services
82106
for _, l2Network := range f.l2Networks {
83-
if strings.HasSuffix(serviceName, "-"+l2Network) {
107+
if strings.HasSuffix(serviceName, "-"+l2Network.Name) || strings.HasSuffix(serviceName, "-"+l2Network.NetworkID) {
84108
return "", 0, false
85109
}
86110
}
87111

112+
// supervisor is special: itcovers multiple networks, so we need to
113+
// identify the depset this chain belongs to
114+
if strings.HasPrefix(serviceName, "op-supervisor") {
115+
for dsName, ds := range s.DepSets {
116+
suffix := "-" + dsName
117+
if !strings.HasSuffix(serviceName, suffix) {
118+
// not the right depset for this supervisor, skip it
119+
continue
120+
}
121+
var depSet depset.StaticConfigDependencySet
122+
if err := json.Unmarshal(ds, &depSet); err != nil {
123+
return "", 0, false
124+
}
125+
var chainID eth.ChainID
126+
if err := chainID.UnmarshalText([]byte(s.NetworkID)); err != nil {
127+
return "", 0, false
128+
}
129+
if depSet.HasChain(chainID) {
130+
name := strings.TrimSuffix(serviceName, suffix)
131+
tag, idx := f.serviceTag(strings.TrimPrefix(name, f.l2ServicePrefix))
132+
return tag, idx, true
133+
}
134+
}
135+
// this supervisor is irrelevant to this chain, skip it
136+
return "", 0, false
137+
}
138+
88139
// Some services don't have a network suffix, as they span multiple chains
89-
// TODO(14849): ideally we'd need to handle *partial* chain coverage.
90140
if strings.HasPrefix(serviceName, f.l2ServicePrefix) {
91141
tag, idx := f.serviceTag(strings.TrimPrefix(serviceName, f.l2ServicePrefix))
92142
return tag, idx, true

kurtosis-devnet/pkg/kurtosis/endpoints_test.go

+141-11
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55

66
"github.com/ethereum-optimism/optimism/devnet-sdk/descriptors"
77
"github.com/ethereum-optimism/optimism/kurtosis-devnet/pkg/kurtosis/sources/inspect"
8+
"github.com/ethereum-optimism/optimism/kurtosis-devnet/pkg/kurtosis/sources/spec"
89
"github.com/stretchr/testify/assert"
910
)
1011

@@ -30,7 +31,7 @@ func TestFindRPCEndpoints(t *testing.T) {
3031
"tcp-discovery": {Port: 53504},
3132
}
3233

33-
testServices["op-el-1-op-geth-op-node-op-kurtosis"] = inspect.PortMap{
34+
testServices["op-el-1-op-geth-op-node-1234"] = inspect.PortMap{
3435
"udp-discovery": {Port: 53233},
3536
"engine-rpc": {Port: 53399},
3637
"metrics": {Port: 53400},
@@ -95,7 +96,13 @@ func TestFindRPCEndpoints(t *testing.T) {
9596
name: "find op-kurtosis L2 endpoints",
9697
services: testServices,
9798
findFn: func(f *ServiceFinder) ([]descriptors.Node, descriptors.ServiceMap) {
98-
return f.FindL2Services("op-kurtosis")
99+
return f.FindL2Services(ChainSpec{
100+
ChainSpec: spec.ChainSpec{
101+
Name: "op-kurtosis",
102+
NetworkID: "1234",
103+
},
104+
DepSets: map[string]descriptors.DepSet{},
105+
})
99106
},
100107
wantNodes: []descriptors.Node{
101108
{
@@ -109,7 +116,7 @@ func TestFindRPCEndpoints(t *testing.T) {
109116
},
110117
},
111118
"el": &descriptors.Service{
112-
Name: "op-el-1-op-geth-op-node-op-kurtosis",
119+
Name: "op-el-1-op-geth-op-node-1234",
113120
Endpoints: descriptors.EndpointMap{
114121
"udp-discovery": {Port: 53233},
115122
"engine-rpc": {Port: 53399},
@@ -139,7 +146,13 @@ func TestFindRPCEndpoints(t *testing.T) {
139146
},
140147
},
141148
findFn: func(f *ServiceFinder) ([]descriptors.Node, descriptors.ServiceMap) {
142-
return f.FindL2Services("custom-host")
149+
return f.FindL2Services(ChainSpec{
150+
ChainSpec: spec.ChainSpec{
151+
Name: "custom-host",
152+
NetworkID: "0000",
153+
},
154+
DepSets: map[string]descriptors.DepSet{},
155+
})
143156
},
144157
wantNodes: nil,
145158
wantServices: descriptors.ServiceMap{
@@ -155,7 +168,12 @@ func TestFindRPCEndpoints(t *testing.T) {
155168

156169
for _, tt := range tests {
157170
t.Run(tt.name, func(t *testing.T) {
158-
finder := NewServiceFinder(tt.services, WithL2Networks([]string{"op-kurtosis", "network1", "network2", "custom-host"}))
171+
finder := NewServiceFinder(tt.services, WithL2Networks([]ChainSpec{
172+
{ChainSpec: spec.ChainSpec{Name: "op-kurtosis", NetworkID: "1234"}},
173+
{ChainSpec: spec.ChainSpec{Name: "network1", NetworkID: "1111"}},
174+
{ChainSpec: spec.ChainSpec{Name: "network2", NetworkID: "2222"}},
175+
{ChainSpec: spec.ChainSpec{Name: "custom-host", NetworkID: "0000"}},
176+
}))
159177
gotNodes, gotServices := tt.findFn(finder)
160178
assert.Equal(t, tt.wantNodes, gotNodes)
161179
assert.Equal(t, tt.wantServices, gotServices)
@@ -164,6 +182,28 @@ func TestFindRPCEndpoints(t *testing.T) {
164182
}
165183

166184
func TestFindL2ServicesSkipsOtherNetworks(t *testing.T) {
185+
n1 := ChainSpec{
186+
ChainSpec: spec.ChainSpec{
187+
Name: "network1",
188+
NetworkID: "1111",
189+
},
190+
DepSets: map[string]descriptors.DepSet{},
191+
}
192+
n2 := ChainSpec{
193+
ChainSpec: spec.ChainSpec{
194+
Name: "network2",
195+
NetworkID: "2222",
196+
},
197+
DepSets: map[string]descriptors.DepSet{},
198+
}
199+
n3 := ChainSpec{
200+
ChainSpec: spec.ChainSpec{
201+
Name: "network3",
202+
NetworkID: "3333",
203+
},
204+
DepSets: map[string]descriptors.DepSet{},
205+
}
206+
167207
// Create a service map with services from multiple L2 networks
168208
services := inspect.ServiceMap{
169209
// network1 services
@@ -181,7 +221,7 @@ func TestFindL2ServicesSkipsOtherNetworks(t *testing.T) {
181221
"op-batcher-network2": inspect.PortMap{
182222
"http": {Port: 8081},
183223
},
184-
"op-proposer-network2": inspect.PortMap{
224+
"op-proposer-2222": inspect.PortMap{
185225
"http": {Port: 8083},
186226
},
187227
"op-cl-1-op-node-op-geth-network2": inspect.PortMap{
@@ -205,15 +245,14 @@ func TestFindL2ServicesSkipsOtherNetworks(t *testing.T) {
205245
},
206246
}
207247

208-
// Create a service finder with all networks configured
209248
finder := NewServiceFinder(
210249
services,
211-
WithL2Networks([]string{"network1", "network2", "network3"}),
250+
WithL2Networks([]ChainSpec{n1, n2, n3}),
212251
)
213252

214253
// Test finding services for network2
215254
t.Run("find network2 services", func(t *testing.T) {
216-
nodes, serviceMap := finder.FindL2Services("network2")
255+
nodes, serviceMap := finder.FindL2Services(n2)
217256

218257
// Verify nodes
219258
assert.Len(t, nodes, 1)
@@ -228,7 +267,7 @@ func TestFindL2ServicesSkipsOtherNetworks(t *testing.T) {
228267
assert.Contains(t, serviceMap, "proposer")
229268
assert.Contains(t, serviceMap, "common-service")
230269
assert.Equal(t, "op-batcher-network2", serviceMap["batcher"].Name)
231-
assert.Equal(t, "op-proposer-network2", serviceMap["proposer"].Name)
270+
assert.Equal(t, "op-proposer-2222", serviceMap["proposer"].Name)
232271
assert.Equal(t, "op-common-service", serviceMap["common-service"].Name)
233272

234273
// Verify network1 and network3 services are not included
@@ -240,7 +279,13 @@ func TestFindL2ServicesSkipsOtherNetworks(t *testing.T) {
240279

241280
// Test with a network that doesn't exist
242281
t.Run("find non-existent network services", func(t *testing.T) {
243-
nodes, serviceMap := finder.FindL2Services("non-existent")
282+
nodes, serviceMap := finder.FindL2Services(ChainSpec{
283+
ChainSpec: spec.ChainSpec{
284+
Name: "non-existent",
285+
NetworkID: "plop",
286+
},
287+
DepSets: map[string]descriptors.DepSet{},
288+
})
244289

245290
// Should only find common services
246291
assert.Len(t, nodes, 0)
@@ -311,3 +356,88 @@ func TestServiceTag(t *testing.T) {
311356
})
312357
}
313358
}
359+
360+
func TestFindL2ServicesWithSupervisors(t *testing.T) {
361+
// Create dependency sets as raw JSON
362+
depSet1 := []byte(`{"dependencies":{"1111":{"activationTime":0,"chainIndex":"1111","historyMinTime":0}}}`)
363+
depSet2 := []byte(`{"dependencies":{"2222":{"activationTime":0,"chainIndex":"2222","historyMinTime":0}}}`)
364+
365+
// Create test services with supervisors for different networks
366+
services := inspect.ServiceMap{
367+
// Network1 supervisor with depSet1
368+
"op-supervisor-depset1": inspect.PortMap{
369+
"rpc": {Port: 8080},
370+
},
371+
// Network2 supervisor with depSet2
372+
"op-supervisor-depset2": inspect.PortMap{
373+
"rpc": {Port: 8081},
374+
},
375+
// extra supervisor
376+
"op-supervisor-depset3": inspect.PortMap{
377+
"rpc": {Port: 8083},
378+
},
379+
}
380+
381+
// Create chain specs for the networks
382+
n1 := ChainSpec{
383+
ChainSpec: spec.ChainSpec{
384+
Name: "network1",
385+
NetworkID: "1111",
386+
},
387+
DepSets: map[string]descriptors.DepSet{
388+
"depset1": depSet1,
389+
},
390+
}
391+
n2 := ChainSpec{
392+
ChainSpec: spec.ChainSpec{
393+
Name: "network2",
394+
NetworkID: "2222",
395+
},
396+
DepSets: map[string]descriptors.DepSet{
397+
"depset2": depSet2,
398+
},
399+
}
400+
401+
finder := NewServiceFinder(
402+
services,
403+
WithL2Networks([]ChainSpec{n1, n2}),
404+
)
405+
406+
tests := []struct {
407+
name string
408+
chainSpec ChainSpec
409+
wantName string
410+
wantPort int
411+
}{
412+
{
413+
name: "network1 supervisor",
414+
chainSpec: n1,
415+
wantName: "op-supervisor-depset1",
416+
wantPort: 8080,
417+
},
418+
{
419+
name: "network2 supervisor",
420+
chainSpec: n2,
421+
wantName: "op-supervisor-depset2",
422+
wantPort: 8081,
423+
},
424+
}
425+
426+
for _, tt := range tests {
427+
t.Run(tt.name, func(t *testing.T) {
428+
_, serviceMap := finder.FindL2Services(tt.chainSpec)
429+
430+
// Debug output
431+
t.Logf("Service map: %+v", serviceMap)
432+
for k, v := range serviceMap {
433+
t.Logf("Service %s: %+v", k, v)
434+
}
435+
436+
// Verify supervisor services
437+
assert.Len(t, serviceMap, 1) // just the supervisor service
438+
assert.Contains(t, serviceMap, "supervisor")
439+
assert.Equal(t, tt.wantName, serviceMap["supervisor"].Name)
440+
assert.Equal(t, tt.wantPort, serviceMap["supervisor"].Endpoints["rpc"].Port)
441+
})
442+
}
443+
}

0 commit comments

Comments
 (0)