Skip to content

Commit bf1aa87

Browse files
authored
Add blockchain endpoint filtering to remove outdated endpoints (#2978)
Filter out RPC and WS endpoints that reference old blockchain IDs when adding default blockchain RPCs to sidecar. This prevents stale endpoints from accumulating in the sidecar when blockchains are redeployed with new IDs.
1 parent ef5a1dc commit bf1aa87

File tree

2 files changed

+185
-4
lines changed

2 files changed

+185
-4
lines changed

pkg/application/app.go

Lines changed: 54 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -653,6 +653,50 @@ func (app *Avalanche) UpdateSidecarNetworks(
653653
return nil
654654
}
655655

656+
// extractBlockchainIDFromEndpoint extracts the blockchain ID from an RPC or WS endpoint URL.
657+
// Expected format: {scheme}://{host}/ext/bc/{blockchainID}/{rpc|ws}
658+
// Returns empty string if the blockchain ID cannot be extracted.
659+
func extractBlockchainIDFromEndpoint(endpoint string) string {
660+
// Look for /ext/bc/ pattern followed by the blockchain ID
661+
const bcPrefix = "/ext/bc/"
662+
bcIndex := strings.Index(endpoint, bcPrefix)
663+
if bcIndex == -1 {
664+
return ""
665+
}
666+
667+
// Extract the part after /ext/bc/
668+
afterBC := endpoint[bcIndex+len(bcPrefix):]
669+
670+
// The blockchain ID is everything before the next /
671+
slashIndex := strings.Index(afterBC, "/")
672+
if slashIndex == -1 {
673+
return ""
674+
}
675+
676+
return afterBC[:slashIndex]
677+
}
678+
679+
// filterOutdatedBlockchainEndpoints removes endpoints that contain a blockchain ID
680+
// that doesn't match the current blockchain ID. Endpoints without a blockchain ID
681+
// in the URL are preserved.
682+
func filterOutdatedBlockchainEndpoints(endpoints []string, currentBlockchainID string) []string {
683+
if currentBlockchainID == "" {
684+
return endpoints
685+
}
686+
687+
filtered := []string{}
688+
for _, endpoint := range endpoints {
689+
extractedID := extractBlockchainIDFromEndpoint(endpoint)
690+
// Keep endpoint if:
691+
// 1. It doesn't contain a blockchain ID pattern (extractedID == "")
692+
// 2. It contains the current blockchain ID
693+
if extractedID == "" || extractedID == currentBlockchainID {
694+
filtered = append(filtered, endpoint)
695+
}
696+
}
697+
return filtered
698+
}
699+
656700
// AddDefaultBlockchainRPCsToSidecar, given a list of (public) node URIs, it generates
657701
// the default blockchain RPC and WS endpoints for those URIs, and adds them
658702
// to the blockchain's sidecar as blockchain URLs
@@ -669,11 +713,17 @@ func (app *Avalanche) AddDefaultBlockchainRPCsToSidecar(
669713
if networkInfo.BlockchainID == ids.Empty {
670714
return sc, fmt.Errorf("blockchain %s has not been deployed to %s", blockchainName, networkModel.Name())
671715
}
672-
rpcEndpoints := set.Of(networkInfo.RPCEndpoints...)
673-
wsEndpoints := set.Of(networkInfo.WSEndpoints...)
716+
717+
// Filter out endpoints that reference old blockchain IDs
718+
currentBlockchainID := networkInfo.BlockchainID.String()
719+
filteredRPCEndpoints := filterOutdatedBlockchainEndpoints(networkInfo.RPCEndpoints, currentBlockchainID)
720+
filteredWSEndpoints := filterOutdatedBlockchainEndpoints(networkInfo.WSEndpoints, currentBlockchainID)
721+
722+
rpcEndpoints := set.Of(filteredRPCEndpoints...)
723+
wsEndpoints := set.Of(filteredWSEndpoints...)
674724
for _, nodeURI := range nodeURIs {
675-
rpcEndpoints.Add(models.GetRPCEndpoint(nodeURI, networkInfo.BlockchainID.String()))
676-
wsEndpoints.Add(models.GetWSEndpoint(nodeURI, networkInfo.BlockchainID.String()))
725+
rpcEndpoints.Add(models.GetRPCEndpoint(nodeURI, currentBlockchainID))
726+
wsEndpoints.Add(models.GetWSEndpoint(nodeURI, currentBlockchainID))
677727
}
678728
networkInfo.RPCEndpoints = rpcEndpoints.List()
679729
networkInfo.WSEndpoints = wsEndpoints.List()

pkg/application/app_test.go

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,3 +283,134 @@ func newTestApp(t *testing.T) *Avalanche {
283283
Log: logging.NoLog{},
284284
}
285285
}
286+
287+
func TestExtractBlockchainIDFromEndpoint(t *testing.T) {
288+
tests := []struct {
289+
name string
290+
endpoint string
291+
expected string
292+
}{
293+
{
294+
name: "valid RPC endpoint",
295+
endpoint: "http://127.0.0.1:55067/ext/bc/2ATnxvq9GwPrEPHyULDJqpanFNJecvzSGZ3w5jMzfZAWSmXC6u/rpc",
296+
expected: "2ATnxvq9GwPrEPHyULDJqpanFNJecvzSGZ3w5jMzfZAWSmXC6u",
297+
},
298+
{
299+
name: "valid WS endpoint",
300+
endpoint: "ws://127.0.0.1:55067/ext/bc/X2oDj86zGjCRCy6vd8Cca4FDStMeFAHWc7SUUygPCCfYf9sHh/ws",
301+
expected: "X2oDj86zGjCRCy6vd8Cca4FDStMeFAHWc7SUUygPCCfYf9sHh",
302+
},
303+
{
304+
name: "endpoint without blockchain ID",
305+
endpoint: "http://127.0.0.1:9650/ext/info",
306+
expected: "",
307+
},
308+
{
309+
name: "endpoint with no path after blockchain ID",
310+
endpoint: "http://127.0.0.1:9650/ext/bc/someblockchainid",
311+
expected: "",
312+
},
313+
{
314+
name: "empty endpoint",
315+
endpoint: "",
316+
expected: "",
317+
},
318+
}
319+
320+
for _, tt := range tests {
321+
t.Run(tt.name, func(t *testing.T) {
322+
require := require.New(t)
323+
result := extractBlockchainIDFromEndpoint(tt.endpoint)
324+
require.Equal(tt.expected, result)
325+
})
326+
}
327+
}
328+
329+
func TestFilterOutdatedBlockchainEndpoints(t *testing.T) {
330+
currentBlockchainID := "X2oDj86zGjCRCy6vd8Cca4FDStMeFAHWc7SUUygPCCfYf9sHh"
331+
oldBlockchainID := "2ATnxvq9GwPrEPHyULDJqpanFNJecvzSGZ3w5jMzfZAWSmXC6u"
332+
333+
tests := []struct {
334+
name string
335+
endpoints []string
336+
currentBlockchainID string
337+
expected []string
338+
}{
339+
{
340+
name: "filter out old blockchain ID endpoints",
341+
endpoints: []string{
342+
"http://127.0.0.1:55067/ext/bc/" + oldBlockchainID + "/rpc",
343+
"http://127.0.0.1:60645/ext/bc/" + currentBlockchainID + "/rpc",
344+
},
345+
currentBlockchainID: currentBlockchainID,
346+
expected: []string{
347+
"http://127.0.0.1:60645/ext/bc/" + currentBlockchainID + "/rpc",
348+
},
349+
},
350+
{
351+
name: "preserve endpoints without blockchain ID",
352+
endpoints: []string{
353+
"http://127.0.0.1:55067/ext/bc/" + oldBlockchainID + "/rpc",
354+
"http://127.0.0.1:9650/ext/info",
355+
"http://127.0.0.1:60645/ext/bc/" + currentBlockchainID + "/rpc",
356+
},
357+
currentBlockchainID: currentBlockchainID,
358+
expected: []string{
359+
"http://127.0.0.1:9650/ext/info",
360+
"http://127.0.0.1:60645/ext/bc/" + currentBlockchainID + "/rpc",
361+
},
362+
},
363+
{
364+
name: "keep all current blockchain endpoints",
365+
endpoints: []string{
366+
"http://127.0.0.1:60645/ext/bc/" + currentBlockchainID + "/rpc",
367+
"http://127.0.0.1:60646/ext/bc/" + currentBlockchainID + "/rpc",
368+
},
369+
currentBlockchainID: currentBlockchainID,
370+
expected: []string{
371+
"http://127.0.0.1:60645/ext/bc/" + currentBlockchainID + "/rpc",
372+
"http://127.0.0.1:60646/ext/bc/" + currentBlockchainID + "/rpc",
373+
},
374+
},
375+
{
376+
name: "empty current blockchain ID returns all endpoints",
377+
endpoints: []string{
378+
"http://127.0.0.1:55067/ext/bc/" + oldBlockchainID + "/rpc",
379+
"http://127.0.0.1:60645/ext/bc/" + currentBlockchainID + "/rpc",
380+
},
381+
currentBlockchainID: "",
382+
expected: []string{
383+
"http://127.0.0.1:55067/ext/bc/" + oldBlockchainID + "/rpc",
384+
"http://127.0.0.1:60645/ext/bc/" + currentBlockchainID + "/rpc",
385+
},
386+
},
387+
{
388+
name: "empty endpoints list",
389+
endpoints: []string{},
390+
currentBlockchainID: currentBlockchainID,
391+
expected: []string{},
392+
},
393+
{
394+
name: "mixed WS and RPC endpoints",
395+
endpoints: []string{
396+
"ws://127.0.0.1:55067/ext/bc/" + oldBlockchainID + "/ws",
397+
"http://127.0.0.1:55067/ext/bc/" + oldBlockchainID + "/rpc",
398+
"ws://127.0.0.1:60645/ext/bc/" + currentBlockchainID + "/ws",
399+
"http://127.0.0.1:60645/ext/bc/" + currentBlockchainID + "/rpc",
400+
},
401+
currentBlockchainID: currentBlockchainID,
402+
expected: []string{
403+
"ws://127.0.0.1:60645/ext/bc/" + currentBlockchainID + "/ws",
404+
"http://127.0.0.1:60645/ext/bc/" + currentBlockchainID + "/rpc",
405+
},
406+
},
407+
}
408+
409+
for _, tt := range tests {
410+
t.Run(tt.name, func(t *testing.T) {
411+
require := require.New(t)
412+
result := filterOutdatedBlockchainEndpoints(tt.endpoints, tt.currentBlockchainID)
413+
require.ElementsMatch(tt.expected, result)
414+
})
415+
}
416+
}

0 commit comments

Comments
 (0)