From 55a1f676e1f2da08ca4c24b14ee8a0f807df886b Mon Sep 17 00:00:00 2001 From: Virgil Date: Fri, 3 Apr 2026 23:01:40 +0000 Subject: [PATCH] feat(dns): infer mainchain client from hsd url when unset Co-Authored-By: Virgil --- service.go | 31 ++++++++++++++++----- service_test.go | 72 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 97 insertions(+), 6 deletions(-) diff --git a/service.go b/service.go index 6ab764c..a293731 100644 --- a/service.go +++ b/service.go @@ -193,12 +193,31 @@ func NewService(options ServiceOptions) *Service { } mainchainClient := options.MainchainAliasClient - if mainchainClient == nil && strings.TrimSpace(options.MainchainURL) != "" { - mainchainClient = NewMainchainAliasClient(MainchainClientOptions{ - URL: options.MainchainURL, - Username: options.MainchainUsername, - Password: options.MainchainPassword, - }) + if mainchainClient == nil { + mainchainURL := strings.TrimSpace(options.MainchainURL) + if mainchainURL == "" { + mainchainURL = strings.TrimSpace(options.HSDURL) + } + if mainchainURL != "" { + mainchainPassword := options.MainchainPassword + mainchainUsername := options.MainchainUsername + if strings.TrimSpace(options.MainchainURL) == "" { + if mainchainPassword == "" { + mainchainPassword = options.HSDPassword + if mainchainPassword == "" { + mainchainPassword = options.HSDApiKey + } + } + if mainchainUsername == "" { + mainchainUsername = options.HSDUsername + } + } + mainchainClient = NewMainchainAliasClient(MainchainClientOptions{ + URL: mainchainURL, + Username: mainchainUsername, + Password: mainchainPassword, + }) + } } cached := make(map[string]NameRecords, len(options.Records)) diff --git a/service_test.go b/service_test.go index 9f52e56..f31fe6e 100644 --- a/service_test.go +++ b/service_test.go @@ -2499,6 +2499,78 @@ func TestServiceHandleActionContextPassesThroughToDiscover(t *testing.T) { } } +func TestServiceDiscoverAliasesFallsBackToMainchainAliasRPCUsingHSDURL(t *testing.T) { + var chainAliasCalls int32 + 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 "get_all_alias_details": + atomic.AddInt32(&chainAliasCalls, 1) + responseWriter.Header().Set("Content-Type", "application/json") + _ = json.NewEncoder(responseWriter).Encode(map[string]any{ + "result": []any{ + map[string]any{ + "hns": "gateway.charon.lthn", + }, + }, + }) + 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": "inferred-mainchain-root-1", + }, + }) + case "getnameresource": + atomic.AddInt32(&nameResourceCalls, 1) + responseWriter.Header().Set("Content-Type", "application/json") + _ = json.NewEncoder(responseWriter).Encode(map[string]any{ + "result": map[string]any{ + "a": []string{"10.10.10.10"}, + }, + }) + default: + t.Fatalf("unexpected method: %s", payload.Method) + } + })) + defer server.Close() + + service := NewService(ServiceOptions{ + HSDURL: server.URL, + Records: map[string]NameRecords{}, + }) + + if err := service.DiscoverAliases(context.Background()); err != nil { + t.Fatalf("expected discover to use inferred mainchain URL: %v", err) + } + + record, ok := service.Resolve("gateway.charon.lthn") + if !ok || len(record.A) != 1 || record.A[0] != "10.10.10.10" { + t.Fatalf("expected discovered gateway A record, got %#v (ok=%t)", record, ok) + } + + if atomic.LoadInt32(&chainAliasCalls) != 1 { + t.Fatalf("expected one mainchain alias call, got %d", atomic.LoadInt32(&chainAliasCalls)) + } + if atomic.LoadInt32(&treeRootCalls) != 1 { + t.Fatalf("expected one tree-root call, got %d", atomic.LoadInt32(&treeRootCalls)) + } + if atomic.LoadInt32(&nameResourceCalls) != 1 { + t.Fatalf("expected one name-resource call, got %d", atomic.LoadInt32(&nameResourceCalls)) + } +} + func TestStringActionValueTrimsWhitespaceForRequiredArgument(t *testing.T) { value, err := stringActionValue(map[string]any{ actionArgName: " gateway.charon.lthn ", -- 2.45.3