feat(dns): wire chain alias discoverer path for mainchain discovery
Implemented DiscoverFromMainchainAliases to honor ServiceOptions chain alias discoverers and fallbacks before direct chain RPC, matching RFC chain-action dependency behavior. Added regression tests for action-based discovery and fallback usage. Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
parent
dbc67951fc
commit
fbc46259e4
2 changed files with 167 additions and 5 deletions
|
|
@ -3,6 +3,7 @@ package dns
|
|||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"sync/atomic"
|
||||
|
|
@ -140,3 +141,151 @@ func TestServiceDiscoverFromMainchainAliasesUsesMainchainThenHSD(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestServiceDiscoverFromMainchainAliasesUsesActionDiscoverer(t *testing.T) {
|
||||
var treeRootCalls int32
|
||||
var nameResourceCalls int32
|
||||
|
||||
server := httptest.NewServer(http.HandlerFunc(func(responseWriter http.ResponseWriter, request *http.Request) {
|
||||
var payload struct {
|
||||
Method string `json:"method"`
|
||||
Params []any `json:"params"`
|
||||
}
|
||||
if err := json.NewDecoder(request.Body).Decode(&payload); err != nil {
|
||||
t.Fatalf("unexpected request payload: %v", err)
|
||||
}
|
||||
|
||||
switch payload.Method {
|
||||
case "getblockchaininfo":
|
||||
atomic.AddInt32(&treeRootCalls, 1)
|
||||
responseWriter.Header().Set("Content-Type", "application/json")
|
||||
_ = json.NewEncoder(responseWriter).Encode(map[string]any{
|
||||
"result": map[string]any{
|
||||
"tree_root": "chain-root-1",
|
||||
},
|
||||
})
|
||||
case "getnameresource":
|
||||
atomic.AddInt32(&nameResourceCalls, 1)
|
||||
switch payload.Params[0] {
|
||||
case "gateway.charon.lthn":
|
||||
_ = json.NewEncoder(responseWriter).Encode(map[string]any{
|
||||
"result": map[string]any{
|
||||
"a": []string{"10.10.10.10"},
|
||||
},
|
||||
})
|
||||
case "node.charon.lthn":
|
||||
_ = json.NewEncoder(responseWriter).Encode(map[string]any{
|
||||
"result": map[string]any{
|
||||
"aaaa": []string{"2600:1f1c:7f0:4f01::2"},
|
||||
},
|
||||
})
|
||||
default:
|
||||
t.Fatalf("unexpected alias lookup: %#v", payload.Params)
|
||||
}
|
||||
default:
|
||||
t.Fatalf("unexpected method: %s", payload.Method)
|
||||
}
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
service := NewService(ServiceOptions{
|
||||
ChainAliasDiscoverer: func(_ context.Context) ([]string, error) {
|
||||
return []string{"gateway.charon.lthn", "node.charon.lthn"}, nil
|
||||
},
|
||||
})
|
||||
hsdClient := NewHSDClient(HSDClientOptions{
|
||||
URL: server.URL,
|
||||
})
|
||||
|
||||
if err := service.DiscoverFromMainchainAliases(context.Background(), nil, hsdClient); err != nil {
|
||||
t.Fatalf("expected discover from chain alias discoverer: %v", err)
|
||||
}
|
||||
|
||||
gateway, ok := service.Resolve("gateway.charon.lthn")
|
||||
if !ok || len(gateway.A) != 1 || gateway.A[0] != "10.10.10.10" {
|
||||
t.Fatalf("expected gateway A record, got %#v (ok=%t)", gateway, ok)
|
||||
}
|
||||
node, ok := service.Resolve("node.charon.lthn")
|
||||
if !ok || len(node.AAAA) != 1 || node.AAAA[0] != "2600:1f1c:7f0:4f01::2" {
|
||||
t.Fatalf("expected node AAAA record, got %#v (ok=%t)", node, ok)
|
||||
}
|
||||
|
||||
if atomic.LoadInt32(&treeRootCalls) != 1 || atomic.LoadInt32(&nameResourceCalls) != 2 {
|
||||
t.Fatalf("expected tree-root and name-resource calls, got treeRoot=%d nameResource=%d", atomic.LoadInt32(&treeRootCalls), atomic.LoadInt32(&nameResourceCalls))
|
||||
}
|
||||
}
|
||||
|
||||
func TestServiceDiscoverFromMainchainAliasesFallsBackToFallbackChainAliasDiscoverer(t *testing.T) {
|
||||
var treeRootCalls int32
|
||||
var nameResourceCalls int32
|
||||
|
||||
server := httptest.NewServer(http.HandlerFunc(func(responseWriter http.ResponseWriter, request *http.Request) {
|
||||
var payload struct {
|
||||
Method string `json:"method"`
|
||||
Params []any `json:"params"`
|
||||
}
|
||||
if err := json.NewDecoder(request.Body).Decode(&payload); err != nil {
|
||||
t.Fatalf("unexpected request payload: %v", err)
|
||||
}
|
||||
|
||||
switch payload.Method {
|
||||
case "getblockchaininfo":
|
||||
atomic.AddInt32(&treeRootCalls, 1)
|
||||
responseWriter.Header().Set("Content-Type", "application/json")
|
||||
_ = json.NewEncoder(responseWriter).Encode(map[string]any{
|
||||
"result": map[string]any{
|
||||
"tree_root": "chain-root-1",
|
||||
},
|
||||
})
|
||||
case "getnameresource":
|
||||
atomic.AddInt32(&nameResourceCalls, 1)
|
||||
switch payload.Params[0] {
|
||||
case "gateway.charon.lthn":
|
||||
_ = json.NewEncoder(responseWriter).Encode(map[string]any{
|
||||
"result": map[string]any{
|
||||
"a": []string{"10.10.10.10"},
|
||||
},
|
||||
})
|
||||
case "node.charon.lthn":
|
||||
_ = json.NewEncoder(responseWriter).Encode(map[string]any{
|
||||
"result": map[string]any{
|
||||
"aaaa": []string{"2600:1f1c:7f0:4f01::2"},
|
||||
},
|
||||
})
|
||||
default:
|
||||
t.Fatalf("unexpected alias lookup: %#v", payload.Params)
|
||||
}
|
||||
default:
|
||||
t.Fatalf("unexpected method: %s", payload.Method)
|
||||
}
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
service := NewService(ServiceOptions{
|
||||
ChainAliasDiscoverer: func(_ context.Context) ([]string, error) {
|
||||
return nil, errors.New("blockchain service unavailable")
|
||||
},
|
||||
FallbackChainAliasDiscoverer: func(_ context.Context) ([]string, error) {
|
||||
return []string{"gateway.charon.lthn", "node.charon.lthn"}, nil
|
||||
},
|
||||
})
|
||||
hsdClient := NewHSDClient(HSDClientOptions{
|
||||
URL: server.URL,
|
||||
})
|
||||
|
||||
if err := service.DiscoverFromMainchainAliases(context.Background(), nil, hsdClient); err != nil {
|
||||
t.Fatalf("expected fallback discover from chain alias discoverer: %v", err)
|
||||
}
|
||||
|
||||
gateway, ok := service.Resolve("gateway.charon.lthn")
|
||||
if !ok || len(gateway.A) != 1 || gateway.A[0] != "10.10.10.10" {
|
||||
t.Fatalf("expected gateway A record, got %#v (ok=%t)", gateway, ok)
|
||||
}
|
||||
node, ok := service.Resolve("node.charon.lthn")
|
||||
if !ok || len(node.AAAA) != 1 || node.AAAA[0] != "2600:1f1c:7f0:4f01::2" {
|
||||
t.Fatalf("expected node AAAA record, got %#v (ok=%t)", node, ok)
|
||||
}
|
||||
|
||||
if atomic.LoadInt32(&treeRootCalls) != 1 || atomic.LoadInt32(&nameResourceCalls) != 2 {
|
||||
t.Fatalf("expected fallback discovery to still trigger RPC calls, got treeRoot=%d nameResource=%d", atomic.LoadInt32(&treeRootCalls), atomic.LoadInt32(&nameResourceCalls))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
23
service.go
23
service.go
|
|
@ -105,11 +105,24 @@ func (service *Service) DiscoverFromChainAliases(ctx context.Context, client *HS
|
|||
// URL: "http://127.0.0.1:14037",
|
||||
// }))
|
||||
func (service *Service) DiscoverFromMainchainAliases(ctx context.Context, chainClient *MainchainAliasClient, hsdClient *HSDClient) error {
|
||||
if chainClient == nil {
|
||||
return fmt.Errorf("mainchain alias client is required")
|
||||
}
|
||||
|
||||
aliases, err := chainClient.GetAllAliasDetails(ctx)
|
||||
aliases, err := discoverAliases(
|
||||
ctx,
|
||||
func(ctx context.Context) ([]string, error) {
|
||||
if service.chainAliasDiscoverer != nil {
|
||||
return service.chainAliasDiscoverer(ctx)
|
||||
}
|
||||
if chainClient == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return chainClient.GetAllAliasDetails(ctx)
|
||||
},
|
||||
func(ctx context.Context) ([]string, error) {
|
||||
if service.fallbackChainAliasDiscoverer != nil {
|
||||
return service.fallbackChainAliasDiscoverer(ctx)
|
||||
}
|
||||
return nil, nil
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue