[agent/codex:gpt-5.3-codex-spark] Read docs/RFC.md fully. Find ONE feature described in the sp... #9
2 changed files with 156 additions and 14 deletions
74
service.go
74
service.go
|
|
@ -30,18 +30,22 @@ type ResolveAddressResult struct {
|
|||
}
|
||||
|
||||
type Service struct {
|
||||
mu sync.RWMutex
|
||||
records map[string]NameRecords
|
||||
reverseIndex map[string][]string
|
||||
treeRoot string
|
||||
discoverer func() (map[string]NameRecords, error)
|
||||
fallbackDiscoverer func() (map[string]NameRecords, error)
|
||||
mu sync.RWMutex
|
||||
records map[string]NameRecords
|
||||
reverseIndex map[string][]string
|
||||
treeRoot string
|
||||
discoverer func() (map[string]NameRecords, error)
|
||||
fallbackDiscoverer func() (map[string]NameRecords, error)
|
||||
chainAliasDiscoverer func(context.Context) ([]string, error)
|
||||
fallbackChainAliasDiscoverer func(context.Context) ([]string, error)
|
||||
}
|
||||
|
||||
type ServiceOptions struct {
|
||||
Records map[string]NameRecords
|
||||
Discoverer func() (map[string]NameRecords, error)
|
||||
FallbackDiscoverer func() (map[string]NameRecords, error)
|
||||
Records map[string]NameRecords
|
||||
Discoverer func() (map[string]NameRecords, error)
|
||||
FallbackDiscoverer func() (map[string]NameRecords, error)
|
||||
ChainAliasDiscoverer func(context.Context) ([]string, error)
|
||||
FallbackChainAliasDiscoverer func(context.Context) ([]string, error)
|
||||
}
|
||||
|
||||
func NewService(options ServiceOptions) *Service {
|
||||
|
|
@ -51,14 +55,56 @@ func NewService(options ServiceOptions) *Service {
|
|||
}
|
||||
treeRoot := computeTreeRoot(cached)
|
||||
return &Service{
|
||||
records: cached,
|
||||
reverseIndex: buildReverseIndex(cached),
|
||||
treeRoot: treeRoot,
|
||||
discoverer: options.Discoverer,
|
||||
fallbackDiscoverer: options.FallbackDiscoverer,
|
||||
records: cached,
|
||||
reverseIndex: buildReverseIndex(cached),
|
||||
treeRoot: treeRoot,
|
||||
discoverer: options.Discoverer,
|
||||
fallbackDiscoverer: options.FallbackDiscoverer,
|
||||
chainAliasDiscoverer: options.ChainAliasDiscoverer,
|
||||
fallbackChainAliasDiscoverer: options.FallbackChainAliasDiscoverer,
|
||||
}
|
||||
}
|
||||
|
||||
func (service *Service) DiscoverFromChainAliases(ctx context.Context, client *HSDClient) error {
|
||||
if client == nil {
|
||||
return fmt.Errorf("hsd client is required")
|
||||
}
|
||||
|
||||
aliases, err := discoverAliases(ctx, service.chainAliasDiscoverer, service.fallbackChainAliasDiscoverer)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if aliases == nil {
|
||||
return nil
|
||||
}
|
||||
return service.DiscoverWithHSD(ctx, aliases, client)
|
||||
}
|
||||
|
||||
func discoverAliases(ctx context.Context, discoverer func(context.Context) ([]string, error), fallback func(context.Context) ([]string, error)) ([]string, error) {
|
||||
if discoverer == nil {
|
||||
if fallback == nil {
|
||||
return nil, nil
|
||||
}
|
||||
aliases, err := fallback(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return aliases, nil
|
||||
}
|
||||
|
||||
aliases, err := discoverer(ctx)
|
||||
if err != nil {
|
||||
if fallback == nil {
|
||||
return nil, err
|
||||
}
|
||||
aliases, err = fallback(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return aliases, nil
|
||||
}
|
||||
|
||||
func (service *Service) Discover() error {
|
||||
discoverer := service.discoverer
|
||||
fallback := service.fallbackDiscoverer
|
||||
|
|
|
|||
|
|
@ -1,7 +1,11 @@
|
|||
package dns
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
|
@ -332,6 +336,98 @@ func TestServiceDiscoverUsesFallbackOnlyWhenPrimaryMissing(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestServiceDiscoverFromChainAliasesUsesFallbackWhenPrimaryFails(t *testing.T) {
|
||||
primaryCalled := false
|
||||
fallbackCalled := false
|
||||
|
||||
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.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 query: %#v", payload.Params)
|
||||
}
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
service := NewService(ServiceOptions{
|
||||
ChainAliasDiscoverer: func(_ context.Context) ([]string, error) {
|
||||
primaryCalled = true
|
||||
return nil, errors.New("blockchain service unavailable")
|
||||
},
|
||||
FallbackChainAliasDiscoverer: func(_ context.Context) ([]string, error) {
|
||||
fallbackCalled = true
|
||||
return []string{"gateway.charon.lthn", "node.charon.lthn"}, nil
|
||||
},
|
||||
})
|
||||
|
||||
client := NewHSDClient(HSDClientOptions{
|
||||
URL: server.URL,
|
||||
})
|
||||
if err := service.DiscoverFromChainAliases(context.Background(), client); err != nil {
|
||||
t.Fatalf("expected chain alias discovery to complete: %v", err)
|
||||
}
|
||||
if !primaryCalled {
|
||||
t.Fatal("expected primary chain alias discoverer to be attempted")
|
||||
}
|
||||
if !fallbackCalled {
|
||||
t.Fatal("expected fallback chain alias discoverer to run")
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
func TestServiceDiscoverFromChainAliasesIgnoresMissingDiscoverers(t *testing.T) {
|
||||
server := httptest.NewServer(http.HandlerFunc(func(responseWriter http.ResponseWriter, request *http.Request) {
|
||||
t.Fatalf("expected no hsd requests when alias discoverers are missing")
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
service := NewService(ServiceOptions{
|
||||
Records: map[string]NameRecords{
|
||||
"gateway.charon.lthn": {
|
||||
A: []string{"10.10.10.10"},
|
||||
},
|
||||
},
|
||||
})
|
||||
client := NewHSDClient(HSDClientOptions{
|
||||
URL: server.URL,
|
||||
})
|
||||
|
||||
if err := service.DiscoverFromChainAliases(context.Background(), client); err != nil {
|
||||
t.Fatalf("expected no-op when no alias discoverer configured: %v", err)
|
||||
}
|
||||
|
||||
result, ok := service.Resolve("gateway.charon.lthn")
|
||||
if !ok || len(result.A) != 1 || result.A[0] != "10.10.10.10" {
|
||||
t.Fatalf("expected baseline record to stay in cache, got %#v (ok=%t)", result, ok)
|
||||
}
|
||||
}
|
||||
|
||||
func TestServiceDiscoverReturnsNilWithoutDiscoverer(t *testing.T) {
|
||||
service := NewService(ServiceOptions{
|
||||
Records: map[string]NameRecords{
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue