diff --git a/api/api.go b/api/api.go index 856db8da..85287a07 100644 --- a/api/api.go +++ b/api/api.go @@ -757,11 +757,11 @@ func (api *api) SendSpontaneousPaymentProbes(ctx context.Context, sendSpontaneou return &SendSpontaneousPaymentProbesResponse{Error: errMessage}, nil } -func (api *api) GetNetworkGraph(nodeIds []string) (NetworkGraphResponse, error) { +func (api *api) GetNetworkGraph(ctx context.Context, nodeIds []string) (NetworkGraphResponse, error) { if api.svc.GetLNClient() == nil { return nil, errors.New("LNClient not started") } - return api.svc.GetLNClient().GetNetworkGraph(nodeIds) + return api.svc.GetLNClient().GetNetworkGraph(ctx, nodeIds) } func (api *api) SyncWallet() error { diff --git a/api/models.go b/api/models.go index 5c514b8b..28bd0719 100644 --- a/api/models.go +++ b/api/models.go @@ -46,7 +46,7 @@ type API interface { Setup(ctx context.Context, setupRequest *SetupRequest) error SendPaymentProbes(ctx context.Context, sendPaymentProbesRequest *SendPaymentProbesRequest) (*SendPaymentProbesResponse, error) SendSpontaneousPaymentProbes(ctx context.Context, sendSpontaneousPaymentProbesRequest *SendSpontaneousPaymentProbesRequest) (*SendSpontaneousPaymentProbesResponse, error) - GetNetworkGraph(nodeIds []string) (NetworkGraphResponse, error) + GetNetworkGraph(ctx context.Context, nodeIds []string) (NetworkGraphResponse, error) SyncWallet() error GetLogOutput(ctx context.Context, logType string, getLogRequest *GetLogOutputRequest) (*GetLogOutputResponse, error) RequestLSPOrder(ctx context.Context, request *LSPOrderRequest) (*LSPOrderResponse, error) diff --git a/http/http_service.go b/http/http_service.go index b5ae1c8d..9c769c15 100644 --- a/http/http_service.go +++ b/http/http_service.go @@ -414,9 +414,11 @@ func (httpSvc *HttpService) nodeStatusHandler(c echo.Context) error { } func (httpSvc *HttpService) nodeNetworkGraphHandler(c echo.Context) error { + ctx := c.Request().Context() + nodeIds := strings.Split(c.QueryParam("nodeIds"), ",") - info, err := httpSvc.api.GetNetworkGraph(nodeIds) + info, err := httpSvc.api.GetNetworkGraph(ctx, nodeIds) if err != nil { return c.JSON(http.StatusInternalServerError, ErrorResponse{ diff --git a/lnclient/breez/breez.go b/lnclient/breez/breez.go index 3e44672e..a568f8c1 100644 --- a/lnclient/breez/breez.go +++ b/lnclient/breez/breez.go @@ -467,7 +467,7 @@ func (bs *BreezService) GetStorageDir() (string, error) { return "", nil } -func (bs *BreezService) GetNetworkGraph(nodeIds []string) (lnclient.NetworkGraphResponse, error) { +func (bs *BreezService) GetNetworkGraph(ctx context.Context, nodeIds []string) (lnclient.NetworkGraphResponse, error) { return nil, nil } diff --git a/lnclient/cashu/cashu.go b/lnclient/cashu/cashu.go index fe9d50f9..92a71813 100644 --- a/lnclient/cashu/cashu.go +++ b/lnclient/cashu/cashu.go @@ -239,7 +239,7 @@ func (cs *CashuService) GetLogOutput(ctx context.Context, maxLen int) ([]byte, e func (cs *CashuService) GetStorageDir() (string, error) { return "", nil } -func (cs *CashuService) GetNetworkGraph(nodeIds []string) (lnclient.NetworkGraphResponse, error) { +func (cs *CashuService) GetNetworkGraph(ctx context.Context, nodeIds []string) (lnclient.NetworkGraphResponse, error) { return nil, nil } func (cs *CashuService) UpdateLastWalletSyncRequest() {} diff --git a/lnclient/greenlight/greenlight.go b/lnclient/greenlight/greenlight.go index 6c33428b..b6e06b74 100644 --- a/lnclient/greenlight/greenlight.go +++ b/lnclient/greenlight/greenlight.go @@ -669,7 +669,7 @@ func (gs *GreenlightService) GetNodeStatus(ctx context.Context) (nodeStatus *lnc return nil, nil } -func (gs *GreenlightService) GetNetworkGraph(nodeIds []string) (lnclient.NetworkGraphResponse, error) { +func (gs *GreenlightService) GetNetworkGraph(ctx context.Context, nodeIds []string) (lnclient.NetworkGraphResponse, error) { return nil, nil } diff --git a/lnclient/ldk/ldk.go b/lnclient/ldk/ldk.go index 1b39efd0..693d7592 100644 --- a/lnclient/ldk/ldk.go +++ b/lnclient/ldk/ldk.go @@ -1163,7 +1163,7 @@ func (ls *LDKService) ListPeers(ctx context.Context) ([]lnclient.PeerDetails, er return ret, nil } -func (ls *LDKService) GetNetworkGraph(nodeIds []string) (lnclient.NetworkGraphResponse, error) { +func (ls *LDKService) GetNetworkGraph(ctx context.Context, nodeIds []string) (lnclient.NetworkGraphResponse, error) { graph := ls.node.NetworkGraph() type NodeInfoWithId struct { diff --git a/lnclient/lnd/lnd.go b/lnclient/lnd/lnd.go index 2d1ace0a..da0382b1 100644 --- a/lnclient/lnd/lnd.go +++ b/lnclient/lnd/lnd.go @@ -4,9 +4,11 @@ import ( "context" "crypto/sha256" "encoding/hex" + "encoding/json" "errors" "fmt" "math" + "slices" "sort" "strconv" "strings" @@ -579,16 +581,40 @@ func (svc *LNDService) Shutdown() error { } func (svc *LNDService) GetNodeConnectionInfo(ctx context.Context) (nodeConnectionInfo *lnclient.NodeConnectionInfo, err error) { - info, err := svc.client.GetInfo(ctx, &lnrpc.GetInfoRequest{}) + pubkey := svc.GetPubkey() + nodeConnectionInfo = &lnclient.NodeConnectionInfo{ + Pubkey: pubkey, + } + + nodeInfo, err := svc.client.GetNodeInfo(ctx, &lnrpc.NodeInfoRequest{ + PubKey: pubkey, + }) if err != nil { - return nil, err + return nodeConnectionInfo, nil } - return &lnclient.NodeConnectionInfo{ - Pubkey: info.IdentityPubkey, - //Address: address, - //Port: port, - }, nil + addresses := nodeInfo.Node.Addresses + if addresses == nil || len(addresses) < 1 { + logger.Logger.Error("Error getting node address info: no available listening addresses") + return nodeConnectionInfo, nil + } + + firstAddress := addresses[0] + parts := strings.Split(firstAddress.Addr, ":") + if len(parts) != 2 { + logger.Logger.WithError(err).Error("Error fetching node address info") + return nodeConnectionInfo, nil + } + port, err := strconv.Atoi(parts[1]) + if err != nil { + logger.Logger.WithError(err).Error("Error getting node address info") + return nodeConnectionInfo, nil + } + + nodeConnectionInfo.Address = parts[0] + nodeConnectionInfo.Port = port + + return nodeConnectionInfo, nil } func (svc *LNDService) ConnectPeer(ctx context.Context, connectPeerRequest *lnclient.ConnectPeerRequest) error { @@ -737,15 +763,34 @@ func (svc *LNDService) GetOnchainBalance(ctx context.Context) (*lnclient.Onchain } func (svc *LNDService) RedeemOnchainFunds(ctx context.Context, toAddress string) (txId string, err error) { - return "", nil + resp, err := svc.client.SendCoins(ctx, &lnrpc.SendCoinsRequest{ + Addr: toAddress, + SendAll: true, + }) + if err != nil { + return "", err + } + return resp.Txid, nil } -func (svc *LNDService) SendPaymentProbes(ctx context.Context, invoice string) error { - return nil -} +func (svc *LNDService) GetLogOutput(ctx context.Context, maxLen int) ([]byte, error) { + resp, err := svc.client.GetDebugInfo(ctx, &lnrpc.GetDebugInfoRequest{}) + if err != nil { + return nil, err + } + jsonBytes, err := json.MarshalIndent(resp.Log, "", "") + if err != nil { + return nil, err + } -func (svc *LNDService) SendSpontaneousPaymentProbes(ctx context.Context, amountMsat uint64, nodeId string) error { - return nil + jsonLength := len(jsonBytes) + start := jsonLength - maxLen + if maxLen == 0 || start < 0 { + start = 0 + } + slicedBytes := jsonBytes[start:] + + return slicedBytes, nil } func (svc *LNDService) ListPeers(ctx context.Context) ([]lnclient.PeerDetails, error) { @@ -762,10 +807,6 @@ func (svc *LNDService) ListPeers(ctx context.Context) ([]lnclient.PeerDetails, e return ret, err } -func (svc *LNDService) GetLogOutput(ctx context.Context, maxLen int) ([]byte, error) { - return []byte{}, nil -} - func (svc *LNDService) SignMessage(ctx context.Context, message string) (string, error) { resp, err := svc.client.SignMessage(ctx, &lnrpc.SignMessageRequest{Msg: []byte(message)}) if err != nil { @@ -901,23 +942,76 @@ func lndInvoiceToTransaction(invoice *lnrpc.Invoice) *lnclient.Transaction { } } -func (svc *LNDService) ResetRouter(key string) error { - return nil -} +func (svc *LNDService) GetNodeStatus(ctx context.Context) (nodeStatus *lnclient.NodeStatus, err error) { + info, err := svc.GetInfo(ctx) + if err != nil { + return nil, err + } + nodeInfo, err := svc.client.GetNodeInfo(ctx, &lnrpc.NodeInfoRequest{ + PubKey: svc.GetPubkey(), + }) + if err != nil { + return nil, err + } + networkInfo, err := svc.client.GetNetworkInfo(ctx, &lnrpc.NetworkInfoRequest{}) + if err != nil { + return nil, err + } + state, err := svc.client.GetState(ctx, &lnrpc.GetStateRequest{}) + if err != nil { + return nil, err + } + debugInfo, err := svc.client.GetDebugInfo(ctx, &lnrpc.GetDebugInfoRequest{}) + if err != nil { + return nil, err + } -func (svc *LNDService) GetStorageDir() (string, error) { - return "", nil + return &lnclient.NodeStatus{ + InternalNodeStatus: map[string]interface{}{ + "info": info, + "config": debugInfo.Config, + "node_info": nodeInfo, + "network_info": networkInfo, + "wallet_state": state.GetState().String(), + }, + }, nil } -func (svc *LNDService) GetNodeStatus(ctx context.Context) (nodeStatus *lnclient.NodeStatus, err error) { - return nil, nil -} +func (svc *LNDService) GetNetworkGraph(ctx context.Context, nodeIds []string) (lnclient.NetworkGraphResponse, error) { + graph, err := svc.client.DescribeGraph(ctx, &lnrpc.ChannelGraphRequest{}) + if err != nil { + return "", err + } -func (svc *LNDService) GetNetworkGraph(nodeIds []string) (lnclient.NetworkGraphResponse, error) { - return nil, nil -} + type NodeInfoWithId struct { + Node *lnrpc.LightningNode `json:"node"` + NodeId string `json:"nodeId"` + } -func (svc *LNDService) UpdateLastWalletSyncRequest() {} + nodes := []NodeInfoWithId{} + channels := []*lnrpc.ChannelEdge{} + + for _, node := range graph.Nodes { + if slices.Contains(nodeIds, node.PubKey) { + nodes = append(nodes, NodeInfoWithId{ + Node: node, + NodeId: node.PubKey, + }) + } + } + + for _, edge := range graph.Edges { + if slices.Contains(nodeIds, edge.Node1Pub) || slices.Contains(nodeIds, edge.Node2Pub) { + channels = append(channels, edge) + } + } + + networkGraph := map[string]interface{}{ + "nodes": nodes, + "channels": channels, + } + return networkGraph, nil +} func (svc *LNDService) UpdateChannel(ctx context.Context, updateChannelRequest *lnclient.UpdateChannelRequest) error { logger.Logger.WithFields(logrus.Fields{ @@ -987,3 +1081,21 @@ func (svc *LNDService) GetSupportedNIP47NotificationTypes() []string { func (svc *LNDService) GetPubkey() string { return svc.nodeInfo.Pubkey } + +func (svc *LNDService) SendPaymentProbes(ctx context.Context, invoice string) error { + return nil +} + +func (svc *LNDService) SendSpontaneousPaymentProbes(ctx context.Context, amountMsat uint64, nodeId string) error { + return nil +} + +func (svc *LNDService) ResetRouter(key string) error { + return nil +} + +func (svc *LNDService) GetStorageDir() (string, error) { + return "", nil +} + +func (svc *LNDService) UpdateLastWalletSyncRequest() {} diff --git a/lnclient/lnd/wrapper/lnd.go b/lnclient/lnd/wrapper/lnd.go index 39fe0b35..7b3e1bb6 100644 --- a/lnclient/lnd/wrapper/lnd.go +++ b/lnclient/lnd/wrapper/lnd.go @@ -32,6 +32,7 @@ type LNDoptions struct { type LNDWrapper struct { client lnrpc.LightningClient routerClient routerrpc.RouterClient + stateClient lnrpc.StateClient IdentityPubkey string } @@ -84,6 +85,7 @@ func NewLNDclient(lndOptions LNDoptions) (result *LNDWrapper, err error) { return &LNDWrapper{ client: lnClient, routerClient: routerrpc.NewRouterClient(conn), + stateClient: lnrpc.NewStateClient(conn), }, nil } @@ -131,10 +133,30 @@ func (wrapper *LNDWrapper) LookupInvoice(ctx context.Context, req *lnrpc.Payment return wrapper.client.LookupInvoice(ctx, req, options...) } +func (wrapper *LNDWrapper) GetDebugInfo(ctx context.Context, req *lnrpc.GetDebugInfoRequest, options ...grpc.CallOption) (*lnrpc.GetDebugInfoResponse, error) { + return wrapper.client.GetDebugInfo(ctx, req, options...) +} + func (wrapper *LNDWrapper) GetInfo(ctx context.Context, req *lnrpc.GetInfoRequest, options ...grpc.CallOption) (*lnrpc.GetInfoResponse, error) { return wrapper.client.GetInfo(ctx, req, options...) } +func (wrapper *LNDWrapper) GetNetworkInfo(ctx context.Context, req *lnrpc.NetworkInfoRequest, options ...grpc.CallOption) (*lnrpc.NetworkInfo, error) { + return wrapper.client.GetNetworkInfo(ctx, req, options...) +} + +func (wrapper *LNDWrapper) DescribeGraph(ctx context.Context, req *lnrpc.ChannelGraphRequest, options ...grpc.CallOption) (*lnrpc.ChannelGraph, error) { + return wrapper.client.DescribeGraph(ctx, req, options...) +} + +func (wrapper *LNDWrapper) GetState(ctx context.Context, req *lnrpc.GetStateRequest, options ...grpc.CallOption) (*lnrpc.GetStateResponse, error) { + return wrapper.stateClient.GetState(ctx, req, options...) +} + +func (wrapper *LNDWrapper) GetNodeInfo(ctx context.Context, req *lnrpc.NodeInfoRequest, options ...grpc.CallOption) (*lnrpc.NodeInfo, error) { + return wrapper.client.GetNodeInfo(ctx, req, options...) +} + func (wrapper *LNDWrapper) DecodeBolt11(ctx context.Context, bolt11 string, options ...grpc.CallOption) (*lnrpc.PayReq, error) { return wrapper.client.DecodePayReq(ctx, &lnrpc.PayReqString{ PayReq: bolt11, @@ -177,6 +199,10 @@ func (wrapper *LNDWrapper) WalletBalance(ctx context.Context, req *lnrpc.WalletB return wrapper.client.WalletBalance(ctx, req, options...) } +func (wrapper *LNDWrapper) SendCoins(ctx context.Context, req *lnrpc.SendCoinsRequest, options ...grpc.CallOption) (*lnrpc.SendCoinsResponse, error) { + return wrapper.client.SendCoins(ctx, req, options...) +} + func (wrapper *LNDWrapper) NewAddress(ctx context.Context, req *lnrpc.NewAddressRequest, options ...grpc.CallOption) (*lnrpc.NewAddressResponse, error) { return wrapper.client.NewAddress(ctx, req, options...) } diff --git a/lnclient/models.go b/lnclient/models.go index 36d2ee99..3f52bfb6 100644 --- a/lnclient/models.go +++ b/lnclient/models.go @@ -74,7 +74,7 @@ type LNClient interface { GetLogOutput(ctx context.Context, maxLen int) ([]byte, error) SignMessage(ctx context.Context, message string) (string, error) GetStorageDir() (string, error) - GetNetworkGraph(nodeIds []string) (NetworkGraphResponse, error) + GetNetworkGraph(ctx context.Context, nodeIds []string) (NetworkGraphResponse, error) UpdateLastWalletSyncRequest() GetSupportedNIP47Methods() []string GetSupportedNIP47NotificationTypes() []string diff --git a/lnclient/phoenixd/phoenixd.go b/lnclient/phoenixd/phoenixd.go index 781d4695..18b23cba 100644 --- a/lnclient/phoenixd/phoenixd.go +++ b/lnclient/phoenixd/phoenixd.go @@ -516,7 +516,7 @@ func (svc *PhoenixService) GetStorageDir() (string, error) { return "", nil } -func (svc *PhoenixService) GetNetworkGraph(nodeIds []string) (lnclient.NetworkGraphResponse, error) { +func (svc *PhoenixService) GetNetworkGraph(ctx context.Context, nodeIds []string) (lnclient.NetworkGraphResponse, error) { return nil, nil } diff --git a/tests/mock_ln_client.go b/tests/mock_ln_client.go index ae1644d9..973dd6cd 100644 --- a/tests/mock_ln_client.go +++ b/tests/mock_ln_client.go @@ -160,7 +160,7 @@ func (mln *MockLn) GetStorageDir() (string, error) { func (mln *MockLn) GetNodeStatus(ctx context.Context) (nodeStatus *lnclient.NodeStatus, err error) { return nil, nil } -func (mln *MockLn) GetNetworkGraph(nodeIds []string) (lnclient.NetworkGraphResponse, error) { +func (mln *MockLn) GetNetworkGraph(ctx context.Context, nodeIds []string) (lnclient.NetworkGraphResponse, error) { return nil, nil } diff --git a/wails/wails_handlers.go b/wails/wails_handlers.go index 251ef8d5..29597724 100644 --- a/wails/wails_handlers.go +++ b/wails/wails_handlers.go @@ -172,7 +172,7 @@ func (app *WailsApp) WailsRequestRouter(route string, method string, body string switch { case len(networkGraphMatch) == 2: nodeIds := networkGraphMatch[1] - networkGraphResponse, err := app.api.GetNetworkGraph(strings.Split(nodeIds, ",")) + networkGraphResponse, err := app.api.GetNetworkGraph(ctx, strings.Split(nodeIds, ",")) if err != nil { return WailsRequestRouterResponse{Body: nil, Error: err.Error()} }