-
Notifications
You must be signed in to change notification settings - Fork 128
feat: add ldk-server backend #2456
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1487,6 +1487,14 @@ func (api *api) RequestMempoolApi(ctx context.Context, endpoint string) (interfa | |
| } | ||
|
|
||
| if res.StatusCode != http.StatusOK { | ||
| if strings.HasPrefix(endpoint, "/v1/lightning/nodes/") && strings.Contains(string(body), `"error":"Failed to get node"`) { | ||
| logger.Logger.WithFields(logrus.Fields{ | ||
| "endpoint": endpoint, | ||
| "status_code": res.StatusCode, | ||
| }).Debug("Mempool node details unavailable") | ||
| return map[string]interface{}{}, nil | ||
| } | ||
|
|
||
| logger.Logger.WithFields(logrus.Fields{ | ||
| "endpoint": endpoint, | ||
| "status_code": res.StatusCode, | ||
|
|
@@ -1552,7 +1560,7 @@ func (api *api) GetInfo(ctx context.Context) (*InfoResponse, error) { | |
| info.VssSupported = backendType == config.LDKBackendType && api.cfg.GetEnv().LDKVssUrl != "" | ||
| info.LdkVssUrl = api.cfg.GetEnv().LDKVssUrl | ||
| info.DatabaseType = api.db.Dialector.Name() | ||
| info.SupportsBolt12 = backendType == config.LDKBackendType || backendType == config.CLNBackendType | ||
| info.SupportsBolt12 = backendType == config.LDKBackendType || backendType == config.CLNBackendType || backendType == config.LDKServerBackendType | ||
| info.AutoUnlockPasswordEnabled = autoUnlockPassword != "" | ||
| info.AutoUnlockPasswordSupported = api.cfg.GetEnv().IsDefaultClientId() | ||
| info.Relays = []InfoResponseRelay{} | ||
|
|
@@ -1862,6 +1870,33 @@ func (api *api) Setup(ctx context.Context, setupRequest *SetupRequest) error { | |
| } | ||
| } | ||
|
|
||
| if setupRequest.LDKServerAddress != "" { | ||
| err = api.cfg.SetUpdate("LDKServerAddress", setupRequest.LDKServerAddress, setupRequest.UnlockPassword) | ||
| if err != nil { | ||
| logger.Logger.WithError(err).Error("Failed to save ldk-server address") | ||
| return err | ||
| } | ||
| } | ||
| if setupRequest.LDKServerTlsCertFile != "" { | ||
| certBytes, err := os.ReadFile(setupRequest.LDKServerTlsCertFile) | ||
| if err != nil { | ||
| logger.Logger.WithError(err).Error("Failed to read ldk-server TLS cert file") | ||
| return err | ||
| } | ||
| err = api.cfg.SetUpdate("LDKServerTlsCertPem", string(certBytes), setupRequest.UnlockPassword) | ||
|
Comment on lines
+1880
to
+1886
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: # Inspect the setup route and request definition to determine whether the
# certificate path reaches Setup from an unauthenticated boundary.
rg -n -A35 -B20 'setupRequest|Setup\(' http/http_service.go api/models.goRepository: getAlby/hub Length of output: 8407 🏁 Script executed: # Locate the route registration and middleware around the setup handler.
rg -n -A12 -B12 'setupHandler|/setup|SetupCompleted|Require|middleware' http/http_service.goRepository: getAlby/hub Length of output: 6037 Path Traversal (CWE-22): Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal') Reachability: External · Exploitability: Moderate Validate and canonicalize the LDK Server certificate before persisting it. The public 🤖 Prompt for AI AgentsSource: Coding guidelines |
||
| if err != nil { | ||
| logger.Logger.WithError(err).Error("Failed to save ldk-server TLS cert") | ||
| return err | ||
| } | ||
| } | ||
| if setupRequest.LDKServerApiKey != "" { | ||
| err = api.cfg.SetUpdate("LDKServerApiKey", setupRequest.LDKServerApiKey, setupRequest.UnlockPassword) | ||
| if err != nil { | ||
| logger.Logger.WithError(err).Error("Failed to save ldk-server API key") | ||
| return err | ||
| } | ||
| } | ||
|
Comment on lines
+1892
to
+1898
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win 🔎 Supported by static analysis🏁 Script executed: printf '%s\n' '--- applicable repository conventions ---'
find /tmp/coderabbit-repo-knowledge/getalby-hub-c4e535df -maxdepth 2 -type f -name '*.md' -print \
-exec sh -c 'echo "--- $1"; head -80 "$1"' sh {} \;
printf '%s\n' '--- changed hunk and nearby setup handler ---'
git diff -- api/api.go
sed -n '1830,1925p' api/api.go
printf '%s\n' '--- bound definitions and consumers ---'
rg -n -C 3 'LDKServerApiKey|NewLDKServerService|func .*SetUpdate|SetUpdate\(' --glob '*.go' --glob '*.md' .Repository: getAlby/hub Length of output: 50367 🏁 Script executed: printf '%s\n' '--- setup method and request type ---'
sed -n '1725,1815p' api/api.go
sed -n '250,290p' api/models.go
printf '%s\n' '--- LDK server implementation ---'
fd -i '.*ldk.*' .
rg -n -C 5 'func NewLDKServerService|LDKServerApiKey|api[_-]?key|authorization' --glob '*.go' --glob '*.md' ldkserver lnclient service README.md 2>/dev/null
printf '%s\n' '--- documented key format ---'
rg -n -C 4 'LDK_SERVER_API_KEY|LDKServerApiKey|64.?character|hexadecimal|API key' README.md docs . 2>/dev/null | head -160Repository: getAlby/hub Length of output: 20913 🏁 Script executed: printf '%s\n' '--- LDK server constructor and authentication path ---'
sed -n '1,180p' 'lnclient/ldk-server/ldkserver.go'
rg -n -C 6 'apiKey|APIKey|ApiKey|hex.DecodeString|HMAC|hmac|authorization|metadata|credentials' 'lnclient/ldk-server'
printf '%s\n' '--- LDK server tests for key handling ---'
rg -n -C 5 'api.?key|API.?key|NewLDKServerService|invalid|hex' 'lnclient/ldk-server/ldkserver_test.go'Repository: getAlby/hub Length of output: 19732 🏁 Script executed: printf '%s\n' '--- constructor request flow and auth use ---'
sed -n '180,235p' 'lnclient/ldk-server/ldkserver.go'
sed -n '740,815p' 'lnclient/ldk-server/ldkserver.go'
printf '%s\n' '--- setup route binding and existing setup tests ---'
rg -n -C 5 'Setup\(|/setup|SetupRequest|ldkServerApiKey' api http frontend --glob '*.go' --glob '*.ts' --glob '*.tsx'Repository: getAlby/hub Length of output: 31229 Reject malformed LDK Server API keys at the setup boundary.
🤖 Prompt for AI AgentsSource: Coding guidelines |
||
|
|
||
| if setupRequest.CashuMintUrl != "" { | ||
| err = api.cfg.SetUpdate("CashuMintUrl", setupRequest.CashuMintUrl, setupRequest.UnlockPassword) | ||
| if err != nil { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,72 @@ | ||
| import React from "react"; | ||
| import { useNavigate } from "react-router"; | ||
| import Container from "src/components/Container"; | ||
| import TwoColumnLayoutHeader from "src/components/TwoColumnLayoutHeader"; | ||
| import { Button } from "src/components/ui/button"; | ||
| import { Input } from "src/components/ui/input"; | ||
| import { Label } from "src/components/ui/label"; | ||
| import useSetupStore from "src/state/SetupStore"; | ||
|
|
||
| export function LDKServerForm() { | ||
| const navigate = useNavigate(); | ||
| const setupStore = useSetupStore(); | ||
| const [ldkServerAddress, setLdkServerAddress] = React.useState<string>( | ||
| setupStore.nodeInfo.ldkServerAddress || "127.0.0.1:3536" | ||
| ); | ||
| const [ldkServerTlsCertFile, setLdkServerTlsCertFile] = React.useState( | ||
| setupStore.nodeInfo.ldkServerTlsCertFile || "" | ||
| ); | ||
| const [ldkServerApiKey, setLdkServerApiKey] = React.useState<string>( | ||
| setupStore.nodeInfo.ldkServerApiKey || "" | ||
| ); | ||
|
|
||
| function onSubmit(e: React.FormEvent) { | ||
| e.preventDefault(); | ||
| setupStore.updateNodeInfo({ | ||
| backendType: "LDK_SERVER", | ||
| ldkServerAddress, | ||
| ldkServerTlsCertFile, | ||
| ldkServerApiKey, | ||
| }); | ||
| navigate("/setup/security"); | ||
| } | ||
|
|
||
| return ( | ||
| <Container> | ||
| <TwoColumnLayoutHeader | ||
| title="Configure LDK Server" | ||
| description="Connect Hub to an existing ldk-server gRPC endpoint." | ||
| /> | ||
| <form className="w-full grid gap-5 mt-6" onSubmit={onSubmit}> | ||
| <div className="grid gap-1.5"> | ||
| <Label htmlFor="ldk-server-address">gRPC Address</Label> | ||
| <Input | ||
| required | ||
| id="ldk-server-address" | ||
| value={ldkServerAddress} | ||
| onChange={(e) => setLdkServerAddress(e.target.value)} | ||
| /> | ||
| </div> | ||
| <div className="grid gap-1.5"> | ||
| <Label htmlFor="ldk-server-cert">TLS certificate path</Label> | ||
| <Input | ||
| required | ||
| id="ldk-server-cert" | ||
| value={ldkServerTlsCertFile} | ||
| onChange={(e) => setLdkServerTlsCertFile(e.target.value)} | ||
| /> | ||
| </div> | ||
| <div className="grid gap-1.5"> | ||
| <Label htmlFor="ldk-server-api-key">API key (hex)</Label> | ||
| <Input | ||
| required | ||
| id="ldk-server-api-key" | ||
| value={ldkServerApiKey} | ||
| onChange={(e) => setLdkServerApiKey(e.target.value)} | ||
| /> | ||
| </div> | ||
| <Button>Next</Button> | ||
| </form> | ||
| </Container> | ||
| ); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🔎 Supported by static analysis
🏁 Script executed:
Repository: getAlby/hub
Length of output: 7429
🏁 Script executed:
Repository: getAlby/hub
Length of output: 5246
🌐 Web query:
Mempool.space API /v1/lightning/nodes/{pubkey} "Failed to get node" HTTP status💡 Result:
The endpoint
GET /api/v1/lightning/nodes/:pubKeyis the correct official API route for retrieving information about a specific Lightning Network node on mempool.space [1]. If you are encountering a "Failed to get node" error when calling this endpoint, it typically indicates one of the following scenarios: 1. Node Visibility: The most common cause is that the requested node public key does not exist or has not yet been indexed/seen by the mempool.space lightning node [2]. If the node is new or lacks active public channels, it may not be present in the network graph used by the API [3]. 2. Invalid Public Key: Ensure thepubkeyprovided is a valid hex-encoded string. 3. Network/Connectivity: Temporary network issues or rate limiting may prevent the request from completing successfully. In programmatic implementations, this error is often handled by checking for a specific error message. For example, some client libraries catch the exception and return null if the error message matches: "This node does not exist, or our node is not seeing it yet" [2]. To troubleshoot: - Verify the public key is correct and publicly visible on the Lightning Network. - Check if the API request is returning a 404 status code (Not Found), which confirms the node is unknown to the mempool instance [2]. - Ensure your implementation correctly handles cases where the API returns a non-200 status code [2]. For further details, consult the official REST API documentation at https://mempool.space/docs/api/rest [1].Citations:
Restrict the fallback to HTTP 404 node responses.
RequestMempoolApireturns an empty map with no error for any non-200 response containing"error":"Failed to get node". A server error can therefore be reported as success, and JSON whitespace prevents valid errors from matching. Parse the error field and require HTTP 404. Add tests for 404, server errors, and whitespace-formatted JSON.🤖 Prompt for AI Agents