From 1de9a192c2939b4b6dbc1bf395ade6e08f6fcb29 Mon Sep 17 00:00:00 2001 From: im-adithya Date: Thu, 23 Nov 2023 14:53:43 +0530 Subject: [PATCH 01/17] feat: add get info method --- alby.go | 61 +++++++++++++++++++++++++++++++ handle_info_request.go | 81 ++++++++++++++++++++++++++++++++++++++++++ lnd.go | 9 +++++ models.go | 12 +++++++ service_test.go | 4 +++ 5 files changed, 167 insertions(+) create mode 100644 handle_info_request.go diff --git a/alby.go b/alby.go index 0248e13a..1446298f 100644 --- a/alby.go +++ b/alby.go @@ -269,6 +269,67 @@ func (svc *AlbyOAuthService) LookupInvoice(ctx context.Context, senderPubkey str return "", false, errors.New(errorPayload.Message) } +func (svc *AlbyOAuthService) GetInfo(ctx context.Context, senderPubkey string) (alias, color, pubkey, network string, block_height uint32, block_hash string, err error) { + alias = "getalby.com" + app := App{} + err = svc.db.Preload("User").First(&app, &App{ + NostrPubkey: senderPubkey, + }).Error + if err != nil { + svc.Logger.WithFields(logrus.Fields{ + "senderPubkey": senderPubkey, + }).Errorf("App not found: %v", err) + return "", "", "", "", 0, "", err + } + tok, err := svc.FetchUserToken(ctx, app) + if err != nil { + return "", "", "", "", 0, "", err + } + client := svc.oauthConf.Client(ctx, tok) + + req, err := http.NewRequest("GET", fmt.Sprintf("%s/getinfo", svc.cfg.AlbyAPIURL), nil) + if err != nil { + svc.Logger.WithError(err).Error("Error creating request /getinfo") + return "", "", "", "", 0, "", err + } + + req.Header.Set("User-Agent", "NWC") + + resp, err := client.Do(req) + if err != nil { + svc.Logger.WithFields(logrus.Fields{ + "senderPubkey": senderPubkey, + "appId": app.ID, + "userId": app.User.ID, + }).Errorf("Failed to fetch node info: %v", err) + return "", "", "", "", 0, "", err + } + + if resp.StatusCode < 300 { + // TODO: decode fetched node info + // err = json.NewDecoder(resp.Body).Decode(&nodeInfo) + if err != nil { + return "", "", "", "", 0, "", err + } + svc.Logger.WithFields(logrus.Fields{ + "senderPubkey": senderPubkey, + "appId": app.ID, + "userId": app.User.ID, + }).Info("Info fetch successful") + return alias, "color", "pubkey", "mainnet", 0, "blockhash", err + } + + errorPayload := &ErrorResponse{} + err = json.NewDecoder(resp.Body).Decode(errorPayload) + svc.Logger.WithFields(logrus.Fields{ + "senderPubkey": senderPubkey, + "appId": app.ID, + "userId": app.User.ID, + "APIHttpStatus": resp.StatusCode, + }).Errorf("Invoices listing failed %s", string(errorPayload.Message)) + return "", "", "", "", 0, "", errors.New(errorPayload.Message) +} + func (svc *AlbyOAuthService) GetBalance(ctx context.Context, senderPubkey string) (balance int64, err error) { app := App{} err = svc.db.Preload("User").First(&app, &App{ diff --git a/handle_info_request.go b/handle_info_request.go new file mode 100644 index 00000000..74fbfa92 --- /dev/null +++ b/handle_info_request.go @@ -0,0 +1,81 @@ +package main + +import ( + "context" + "fmt" + + "github.com/nbd-wtf/go-nostr" + "github.com/sirupsen/logrus" +) + +func (svc *Service) HandleGetInfoEvent(ctx context.Context, request *Nip47Request, event *nostr.Event, app App, ss []byte) (result *nostr.Event, err error) { + + nostrEvent := NostrEvent{App: app, NostrId: event.ID, Content: event.Content, State: "received"} + err = svc.db.Create(&nostrEvent).Error + if err != nil { + svc.Logger.WithFields(logrus.Fields{ + "eventId": event.ID, + "eventKind": event.Kind, + "appId": app.ID, + }).Errorf("Failed to save nostr event: %v", err) + return nil, err + } + + hasPermission, code, message := svc.hasPermission(&app, event, request.Method, nil) + + if !hasPermission { + svc.Logger.WithFields(logrus.Fields{ + "eventId": event.ID, + "eventKind": event.Kind, + "appId": app.ID, + }).Errorf("App does not have permission: %s %s", code, message) + + return svc.createResponse(event, Nip47Response{ + ResultType: NIP_47_GET_INFO_METHOD, + Error: &Nip47Error{ + Code: code, + Message: message, + }}, ss) + } + + svc.Logger.WithFields(logrus.Fields{ + "eventId": event.ID, + "eventKind": event.Kind, + "appId": app.ID, + }).Info("Fetching node info") + + alias, color, pubkey, network, block_height, block_hash, err := svc.lnClient.GetInfo(ctx, event.PubKey) + if err != nil { + svc.Logger.WithFields(logrus.Fields{ + "eventId": event.ID, + "eventKind": event.Kind, + "appId": app.ID, + }).Infof("Failed to fetch node info: %v", err) + nostrEvent.State = NOSTR_EVENT_STATE_HANDLER_ERROR + svc.db.Save(&nostrEvent) + return svc.createResponse(event, Nip47Response{ + ResultType: NIP_47_GET_BALANCE_METHOD, + Error: &Nip47Error{ + Code: NIP_47_ERROR_INTERNAL, + Message: fmt.Sprintf("Something went wrong while fetching node info: %s", err.Error()), + }, + }, ss) + } + + responsePayload := &Nip47GetInfoResponse{ + Alias: alias, + Color: color, + Pubkey: pubkey, + Network: network, + BlockHeight: block_height, + BlockHash: block_hash, + } + + nostrEvent.State = NOSTR_EVENT_STATE_HANDLER_EXECUTED + svc.db.Save(&nostrEvent) + return svc.createResponse(event, Nip47Response{ + ResultType: NIP_47_GET_BALANCE_METHOD, + Result: responsePayload, + }, + ss) +} diff --git a/lnd.go b/lnd.go index 4993c5e9..ad911257 100644 --- a/lnd.go +++ b/lnd.go @@ -18,6 +18,7 @@ import ( type LNClient interface { SendPaymentSync(ctx context.Context, senderPubkey string, payReq string) (preimage string, err error) GetBalance(ctx context.Context, senderPubkey string) (balance int64, err error) + GetInfo(ctx context.Context, senderPubkey string) (alias, color, pubkey, network string, block_height uint32, block_hash string, err error) MakeInvoice(ctx context.Context, senderPubkey string, amount int64, description string, descriptionHash string, expiry int64) (invoice string, paymentHash string, err error) LookupInvoice(ctx context.Context, senderPubkey string, paymentHash string) (invoice string, paid bool, err error) } @@ -51,6 +52,14 @@ func (svc *LNDService) GetBalance(ctx context.Context, senderPubkey string) (bal return int64(resp.LocalBalance.Sat), nil } +func (svc *LNDService) GetInfo(ctx context.Context, senderPubkey string) (alias, color, pubkey, network string, block_height uint32, block_hash string, err error) { + resp, err := svc.client.GetInfo(ctx, &lnrpc.GetInfoRequest{}) + if err != nil { + return "", "", "", "", 0, "", err + } + return resp.Alias, resp.Color, resp.IdentityPubkey, "mainnet", resp.BlockHeight, resp.BlockHash, nil +} + func (svc *LNDService) MakeInvoice(ctx context.Context, senderPubkey string, amount int64, description string, descriptionHash string, expiry int64) (invoice string, paymentHash string, err error) { var descriptionHashBytes []byte diff --git a/models.go b/models.go index aa5d3352..03abd398 100644 --- a/models.go +++ b/models.go @@ -13,6 +13,7 @@ const ( NIP_47_RESPONSE_KIND = 23195 NIP_47_PAY_INVOICE_METHOD = "pay_invoice" NIP_47_GET_BALANCE_METHOD = "get_balance" + NIP_47_GET_INFO_METHOD = "get_info" NIP_47_MAKE_INVOICE_METHOD = "make_invoice" NIP_47_LOOKUP_INVOICE_METHOD = "lookup_invoice" NIP_47_ERROR_INTERNAL = "INTERNAL" @@ -36,6 +37,7 @@ const ( var nip47MethodDescriptions = map[string]string{ NIP_47_GET_BALANCE_METHOD: "Read your balance", + NIP_47_GET_INFO_METHOD: "Read your node info", NIP_47_PAY_INVOICE_METHOD: "Send payments", NIP_47_MAKE_INVOICE_METHOD: "Create invoices", NIP_47_LOOKUP_INVOICE_METHOD: "Lookup status of invoices", @@ -43,6 +45,7 @@ var nip47MethodDescriptions = map[string]string{ var nip47MethodIcons = map[string]string{ NIP_47_GET_BALANCE_METHOD: "wallet", + NIP_47_GET_INFO_METHOD: "wallet", NIP_47_PAY_INVOICE_METHOD: "lightning", NIP_47_MAKE_INVOICE_METHOD: "invoice", NIP_47_LOOKUP_INVOICE_METHOD: "search", @@ -187,6 +190,15 @@ type Nip47BalanceResponse struct { BudgetRenewal string `json:"budget_renewal"` } +type Nip47GetInfoResponse struct { + Alias string `json:"alias"` + Color string `json:"color"` + Pubkey string `json:"pubkey"` + Network string `json:"network"` + BlockHeight uint32 `json:"block_height"` + BlockHash string `json:"block_hash"` +} + type Nip47MakeInvoiceParams struct { Amount int64 `json:"amount"` Description string `json:"description"` diff --git a/service_test.go b/service_test.go index 40b819fd..3cd99e67 100644 --- a/service_test.go +++ b/service_test.go @@ -444,6 +444,10 @@ func (mln *MockLn) GetBalance(ctx context.Context, senderPubkey string) (balance return 21, nil } +func (mln *MockLn) GetInfo(ctx context.Context, senderPubkey string) (alias, color, pubkey, network string, block_height uint32, block_hash string, err error) { + return "bob", "#3399FF", "123pubkey", "testnet", 12, "123blockhash", nil +} + func (mln *MockLn) MakeInvoice(ctx context.Context, senderPubkey string, amount int64, description string, descriptionHash string, expiry int64) (invoice string, paymentHash string, err error) { return mockInvoice, mockPaymentHash, nil } From c926f34df6ae017020b11917cb940cae83a52be5 Mon Sep 17 00:00:00 2001 From: im-adithya Date: Mon, 27 Nov 2023 12:46:48 +0530 Subject: [PATCH 02/17] chore: refactoring --- alby.go | 25 ++++++++++++++++--------- handle_info_request.go | 20 ++++++++++---------- lnd.go | 15 +++++++++++---- models.go | 9 +++++++++ service.go | 2 ++ service_test.go | 11 +++++++++-- 6 files changed, 57 insertions(+), 25 deletions(-) diff --git a/alby.go b/alby.go index 1446298f..4411ca88 100644 --- a/alby.go +++ b/alby.go @@ -269,8 +269,8 @@ func (svc *AlbyOAuthService) LookupInvoice(ctx context.Context, senderPubkey str return "", false, errors.New(errorPayload.Message) } -func (svc *AlbyOAuthService) GetInfo(ctx context.Context, senderPubkey string) (alias, color, pubkey, network string, block_height uint32, block_hash string, err error) { - alias = "getalby.com" +func (svc *AlbyOAuthService) GetInfo(ctx context.Context, senderPubkey string) (info *NodeInfo, err error) { + alias := "getalby.com" app := App{} err = svc.db.Preload("User").First(&app, &App{ NostrPubkey: senderPubkey, @@ -279,18 +279,18 @@ func (svc *AlbyOAuthService) GetInfo(ctx context.Context, senderPubkey string) ( svc.Logger.WithFields(logrus.Fields{ "senderPubkey": senderPubkey, }).Errorf("App not found: %v", err) - return "", "", "", "", 0, "", err + return nil, err } tok, err := svc.FetchUserToken(ctx, app) if err != nil { - return "", "", "", "", 0, "", err + return nil, err } client := svc.oauthConf.Client(ctx, tok) req, err := http.NewRequest("GET", fmt.Sprintf("%s/getinfo", svc.cfg.AlbyAPIURL), nil) if err != nil { svc.Logger.WithError(err).Error("Error creating request /getinfo") - return "", "", "", "", 0, "", err + return nil, err } req.Header.Set("User-Agent", "NWC") @@ -302,21 +302,28 @@ func (svc *AlbyOAuthService) GetInfo(ctx context.Context, senderPubkey string) ( "appId": app.ID, "userId": app.User.ID, }).Errorf("Failed to fetch node info: %v", err) - return "", "", "", "", 0, "", err + return nil, err } if resp.StatusCode < 300 { // TODO: decode fetched node info // err = json.NewDecoder(resp.Body).Decode(&nodeInfo) if err != nil { - return "", "", "", "", 0, "", err + return nil, err } svc.Logger.WithFields(logrus.Fields{ "senderPubkey": senderPubkey, "appId": app.ID, "userId": app.User.ID, }).Info("Info fetch successful") - return alias, "color", "pubkey", "mainnet", 0, "blockhash", err + return &NodeInfo{ + alias: alias, + color: "color", + pubkey: "pubkey", + network: "mainnet", + block_height: 0, + block_hash: "blockhash", + }, err } errorPayload := &ErrorResponse{} @@ -327,7 +334,7 @@ func (svc *AlbyOAuthService) GetInfo(ctx context.Context, senderPubkey string) ( "userId": app.User.ID, "APIHttpStatus": resp.StatusCode, }).Errorf("Invoices listing failed %s", string(errorPayload.Message)) - return "", "", "", "", 0, "", errors.New(errorPayload.Message) + return nil, errors.New(errorPayload.Message) } func (svc *AlbyOAuthService) GetBalance(ctx context.Context, senderPubkey string) (balance int64, err error) { diff --git a/handle_info_request.go b/handle_info_request.go index 74fbfa92..06c62ae5 100644 --- a/handle_info_request.go +++ b/handle_info_request.go @@ -31,7 +31,7 @@ func (svc *Service) HandleGetInfoEvent(ctx context.Context, request *Nip47Reques }).Errorf("App does not have permission: %s %s", code, message) return svc.createResponse(event, Nip47Response{ - ResultType: NIP_47_GET_INFO_METHOD, + ResultType: request.Method, Error: &Nip47Error{ Code: code, Message: message, @@ -44,7 +44,7 @@ func (svc *Service) HandleGetInfoEvent(ctx context.Context, request *Nip47Reques "appId": app.ID, }).Info("Fetching node info") - alias, color, pubkey, network, block_height, block_hash, err := svc.lnClient.GetInfo(ctx, event.PubKey) + info, err := svc.lnClient.GetInfo(ctx, event.PubKey) if err != nil { svc.Logger.WithFields(logrus.Fields{ "eventId": event.ID, @@ -54,7 +54,7 @@ func (svc *Service) HandleGetInfoEvent(ctx context.Context, request *Nip47Reques nostrEvent.State = NOSTR_EVENT_STATE_HANDLER_ERROR svc.db.Save(&nostrEvent) return svc.createResponse(event, Nip47Response{ - ResultType: NIP_47_GET_BALANCE_METHOD, + ResultType: request.Method, Error: &Nip47Error{ Code: NIP_47_ERROR_INTERNAL, Message: fmt.Sprintf("Something went wrong while fetching node info: %s", err.Error()), @@ -63,18 +63,18 @@ func (svc *Service) HandleGetInfoEvent(ctx context.Context, request *Nip47Reques } responsePayload := &Nip47GetInfoResponse{ - Alias: alias, - Color: color, - Pubkey: pubkey, - Network: network, - BlockHeight: block_height, - BlockHash: block_hash, + Alias: info.alias, + Color: info.color, + Pubkey: info.pubkey, + Network: info.network, + BlockHeight: info.block_height, + BlockHash: info.block_hash, } nostrEvent.State = NOSTR_EVENT_STATE_HANDLER_EXECUTED svc.db.Save(&nostrEvent) return svc.createResponse(event, Nip47Response{ - ResultType: NIP_47_GET_BALANCE_METHOD, + ResultType: request.Method, Result: responsePayload, }, ss) diff --git a/lnd.go b/lnd.go index ad911257..c518a8a0 100644 --- a/lnd.go +++ b/lnd.go @@ -18,7 +18,7 @@ import ( type LNClient interface { SendPaymentSync(ctx context.Context, senderPubkey string, payReq string) (preimage string, err error) GetBalance(ctx context.Context, senderPubkey string) (balance int64, err error) - GetInfo(ctx context.Context, senderPubkey string) (alias, color, pubkey, network string, block_height uint32, block_hash string, err error) + GetInfo(ctx context.Context, senderPubkey string) (info *NodeInfo, err error) MakeInvoice(ctx context.Context, senderPubkey string, amount int64, description string, descriptionHash string, expiry int64) (invoice string, paymentHash string, err error) LookupInvoice(ctx context.Context, senderPubkey string, paymentHash string) (invoice string, paid bool, err error) } @@ -52,12 +52,19 @@ func (svc *LNDService) GetBalance(ctx context.Context, senderPubkey string) (bal return int64(resp.LocalBalance.Sat), nil } -func (svc *LNDService) GetInfo(ctx context.Context, senderPubkey string) (alias, color, pubkey, network string, block_height uint32, block_hash string, err error) { +func (svc *LNDService) GetInfo(ctx context.Context, senderPubkey string) (info *NodeInfo, err error) { resp, err := svc.client.GetInfo(ctx, &lnrpc.GetInfoRequest{}) if err != nil { - return "", "", "", "", 0, "", err + return nil, err } - return resp.Alias, resp.Color, resp.IdentityPubkey, "mainnet", resp.BlockHeight, resp.BlockHash, nil + return &NodeInfo{ + alias: resp.Alias, + color: resp.Color, + pubkey: resp.IdentityPubkey, + network: "mainnet", + block_height: resp.BlockHeight, + block_hash: resp.BlockHash, + }, nil } func (svc *LNDService) MakeInvoice(ctx context.Context, senderPubkey string, amount int64, description string, descriptionHash string, expiry int64) (invoice string, paymentHash string, err error) { diff --git a/models.go b/models.go index 03abd398..36da88bb 100644 --- a/models.go +++ b/models.go @@ -157,6 +157,15 @@ type ErrorResponse struct { Message string `json:"message"` } +type NodeInfo struct { + alias string + color string + pubkey string + network string + block_height uint32 + block_hash string +} + type Identity struct { gorm.Model Privkey string diff --git a/service.go b/service.go index 59281282..04e62640 100644 --- a/service.go +++ b/service.go @@ -197,6 +197,8 @@ func (svc *Service) HandleEvent(ctx context.Context, event *nostr.Event) (result return svc.HandleMakeInvoiceEvent(ctx, nip47Request, event, app, ss) case NIP_47_LOOKUP_INVOICE_METHOD: return svc.HandleLookupInvoiceEvent(ctx, nip47Request, event, app, ss) + case NIP_47_GET_INFO_METHOD: + return svc.HandleGetInfoEvent(ctx, nip47Request, event, app, ss) default: return svc.createResponse(event, Nip47Response{ ResultType: nip47Request.Method, diff --git a/service_test.go b/service_test.go index 3cd99e67..23039de2 100644 --- a/service_test.go +++ b/service_test.go @@ -444,8 +444,15 @@ func (mln *MockLn) GetBalance(ctx context.Context, senderPubkey string) (balance return 21, nil } -func (mln *MockLn) GetInfo(ctx context.Context, senderPubkey string) (alias, color, pubkey, network string, block_height uint32, block_hash string, err error) { - return "bob", "#3399FF", "123pubkey", "testnet", 12, "123blockhash", nil +func (mln *MockLn) GetInfo(ctx context.Context, senderPubkey string) (info *NodeInfo, err error) { + return &NodeInfo{ + alias: "bob", + color: "#3399FF", + pubkey: "123pubkey", + network: "testnet", + block_height: 12, + block_hash: "123blockhash", + }, nil } func (mln *MockLn) MakeInvoice(ctx context.Context, senderPubkey string, amount int64, description string, descriptionHash string, expiry int64) (invoice string, paymentHash string, err error) { From 3b85ac2e255c944852e327b65637b0cf0183eeae Mon Sep 17 00:00:00 2001 From: im-adithya Date: Mon, 11 Dec 2023 13:26:27 +0530 Subject: [PATCH 03/17] chore: fix nodeinfo type --- alby.go | 12 ++++++------ handle_info_request.go | 15 +++++++-------- lnd.go | 12 ++++++------ models.go | 12 ++++++------ service_test.go | 12 ++++++------ 5 files changed, 31 insertions(+), 32 deletions(-) diff --git a/alby.go b/alby.go index 4411ca88..47413833 100644 --- a/alby.go +++ b/alby.go @@ -317,12 +317,12 @@ func (svc *AlbyOAuthService) GetInfo(ctx context.Context, senderPubkey string) ( "userId": app.User.ID, }).Info("Info fetch successful") return &NodeInfo{ - alias: alias, - color: "color", - pubkey: "pubkey", - network: "mainnet", - block_height: 0, - block_hash: "blockhash", + Alias: alias, + Color: "color", + Pubkey: "pubkey", + Network: "mainnet", + BlockHeight: 0, + BlockHash: "blockhash", }, err } diff --git a/handle_info_request.go b/handle_info_request.go index 06c62ae5..a51fc9a2 100644 --- a/handle_info_request.go +++ b/handle_info_request.go @@ -63,12 +63,12 @@ func (svc *Service) HandleGetInfoEvent(ctx context.Context, request *Nip47Reques } responsePayload := &Nip47GetInfoResponse{ - Alias: info.alias, - Color: info.color, - Pubkey: info.pubkey, - Network: info.network, - BlockHeight: info.block_height, - BlockHash: info.block_hash, + Alias: info.Alias, + Color: info.Color, + Pubkey: info.Pubkey, + Network: info.Network, + BlockHeight: info.BlockHeight, + BlockHash: info.BlockHash, } nostrEvent.State = NOSTR_EVENT_STATE_HANDLER_EXECUTED @@ -76,6 +76,5 @@ func (svc *Service) HandleGetInfoEvent(ctx context.Context, request *Nip47Reques return svc.createResponse(event, Nip47Response{ ResultType: request.Method, Result: responsePayload, - }, - ss) + }, ss) } diff --git a/lnd.go b/lnd.go index c518a8a0..243676b4 100644 --- a/lnd.go +++ b/lnd.go @@ -58,12 +58,12 @@ func (svc *LNDService) GetInfo(ctx context.Context, senderPubkey string) (info * return nil, err } return &NodeInfo{ - alias: resp.Alias, - color: resp.Color, - pubkey: resp.IdentityPubkey, - network: "mainnet", - block_height: resp.BlockHeight, - block_hash: resp.BlockHash, + Alias: resp.Alias, + Color: resp.Color, + Pubkey: resp.IdentityPubkey, + Network: "mainnet", + BlockHeight: resp.BlockHeight, + BlockHash: resp.BlockHash, }, nil } diff --git a/models.go b/models.go index 36da88bb..52009052 100644 --- a/models.go +++ b/models.go @@ -158,12 +158,12 @@ type ErrorResponse struct { } type NodeInfo struct { - alias string - color string - pubkey string - network string - block_height uint32 - block_hash string + Alias string + Color string + Pubkey string + Network string + BlockHeight uint32 + BlockHash string } type Identity struct { diff --git a/service_test.go b/service_test.go index 23039de2..6a36c836 100644 --- a/service_test.go +++ b/service_test.go @@ -446,12 +446,12 @@ func (mln *MockLn) GetBalance(ctx context.Context, senderPubkey string) (balance func (mln *MockLn) GetInfo(ctx context.Context, senderPubkey string) (info *NodeInfo, err error) { return &NodeInfo{ - alias: "bob", - color: "#3399FF", - pubkey: "123pubkey", - network: "testnet", - block_height: 12, - block_hash: "123blockhash", + Alias: "bob", + Color: "#3399FF", + Pubkey: "123pubkey", + Network: "testnet", + BlockHeight: 12, + BlockHash: "123blockhash", }, nil } From defad16c1425bd754ccb16a098844040649bc95e Mon Sep 17 00:00:00 2001 From: im-adithya Date: Mon, 11 Dec 2023 13:28:49 +0530 Subject: [PATCH 04/17] chore: add in supported methods --- service.go | 1 + 1 file changed, 1 insertion(+) diff --git a/service.go b/service.go index 04e62640..1d081b04 100644 --- a/service.go +++ b/service.go @@ -26,6 +26,7 @@ type Service struct { var supportedMethods = map[string]bool{ NIP_47_PAY_INVOICE_METHOD: true, NIP_47_GET_BALANCE_METHOD: true, + NIP_47_GET_INFO_METHOD: true, NIP_47_MAKE_INVOICE_METHOD: true, NIP_47_LOOKUP_INVOICE_METHOD: true, } From 85823710ac29a6f5bcc882b5059786fd8ec3f89f Mon Sep 17 00:00:00 2001 From: Roland Bewick Date: Tue, 12 Dec 2023 15:29:35 +0700 Subject: [PATCH 05/17] doc: add supported methods to README --- README.md | 69 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/README.md b/README.md index 38458b27..28b1266c 100644 --- a/README.md +++ b/README.md @@ -117,3 +117,72 @@ Want to support the work on Alby? Support the Alby team ⚡️hello@getalby.com You can also contribute to our [bounty program](https://github.com/getAlby/lightning-browser-extension/wiki/Bounties): ⚡️bounties@getalby.com + + +## NIP-47 Supported Methods + +✅ NIP-47 info event + +### LND + +✅ `get_info` + +✅ `get_balance` + +✅ `pay_invoice` + +⚠️ `make_invoice` +- ⚠️ preimage in response not supported (TBC) +- ⚠️ preimage in response not supported (TBC) +- ⚠️ invoice in response missing (TODO) + +⚠️ `lookup_invoice` +- ⚠️ invoice in response missing (TODO) +- ⚠️ response does not match spec, missing fields + +❌ `pay_keysend` + +❌ `list_transactions` + +❌ `list_invoices` + +❌ `list_payments` + +❌ `multi_pay_invoice (TBC)` + +❌ `multi_pay_keysend (TBC)` + +### Alby OAuth API + +✅ `get_info` +- ⚠️ block_hash not supported +- ⚠️ block_height not supported +- ⚠️ pubkey not supported +- ⚠️ color not supported +- ⚠️ network is always `mainnet` + +✅ `get_balance` + +✅ `pay_invoice` + +⚠️ `make_invoice` +- ⚠️ expiry in request not supported +- ⚠️ preimage in request not supported (TBC) +- ⚠️ preimage in response not supported (TBC) +- ⚠️ invoice in response missing (TODO) + +⚠️ `lookup_invoice` +- ⚠️ invoice in response missing (TODO) +- ⚠️ response does not match spec, missing fields (TODO) + +❌ `pay_keysend` + +❌ `list_transactions` + +❌ `list_invoices` + +❌ `list_payments` + +❌ `multi_pay_invoice (TBC)` + +❌ `multi_pay_keysend (TBC)` From db1d1aafeddf58878e4b4f723193ae3aa4e6fba1 Mon Sep 17 00:00:00 2001 From: Roland Bewick Date: Tue, 12 Dec 2023 15:30:22 +0700 Subject: [PATCH 06/17] feat: support methods in get_info, update nip-47 capabilities --- handle_info_request.go | 7 ++++--- models.go | 16 +++++++++------- service.go | 17 +++++++++++++++++ 3 files changed, 30 insertions(+), 10 deletions(-) diff --git a/handle_info_request.go b/handle_info_request.go index a51fc9a2..799eed18 100644 --- a/handle_info_request.go +++ b/handle_info_request.go @@ -33,9 +33,9 @@ func (svc *Service) HandleGetInfoEvent(ctx context.Context, request *Nip47Reques return svc.createResponse(event, Nip47Response{ ResultType: request.Method, Error: &Nip47Error{ - Code: code, - Message: message, - }}, ss) + Code: code, + Message: message, + }}, ss) } svc.Logger.WithFields(logrus.Fields{ @@ -69,6 +69,7 @@ func (svc *Service) HandleGetInfoEvent(ctx context.Context, request *Nip47Reques Network: info.Network, BlockHeight: info.BlockHeight, BlockHash: info.BlockHash, + Methods: svc.GetMethods(&app), } nostrEvent.State = NOSTR_EVENT_STATE_HANDLER_EXECUTED diff --git a/models.go b/models.go index 52009052..6e30d3aa 100644 --- a/models.go +++ b/models.go @@ -24,7 +24,7 @@ const ( NIP_47_ERROR_EXPIRED = "EXPIRED" NIP_47_ERROR_RESTRICTED = "RESTRICTED" NIP_47_OTHER = "OTHER" - NIP_47_CAPABILITIES = "pay_invoice,get_balance" + NIP_47_CAPABILITIES = "pay_invoice,get_balance,get_info,make_invoice,lookup_invoice" ) const ( @@ -164,6 +164,7 @@ type NodeInfo struct { Network string BlockHeight uint32 BlockHash string + Methods []string } type Identity struct { @@ -200,12 +201,13 @@ type Nip47BalanceResponse struct { } type Nip47GetInfoResponse struct { - Alias string `json:"alias"` - Color string `json:"color"` - Pubkey string `json:"pubkey"` - Network string `json:"network"` - BlockHeight uint32 `json:"block_height"` - BlockHash string `json:"block_hash"` + Alias string `json:"alias"` + Color string `json:"color"` + Pubkey string `json:"pubkey"` + Network string `json:"network"` + BlockHeight uint32 `json:"block_height"` + BlockHash string `json:"block_hash"` + Methods []string `json:"methods"` } type Nip47MakeInvoiceParams struct { diff --git a/service.go b/service.go index abe5286a..61b23857 100644 --- a/service.go +++ b/service.go @@ -4,6 +4,7 @@ import ( "context" "encoding/json" "fmt" + "strings" "time" "github.com/labstack/echo-contrib/session" @@ -243,6 +244,22 @@ func (svc *Service) createResponse(initialEvent *nostr.Event, content interface{ return resp, nil } +func (svc *Service) GetMethods(app *App) []string { + appPermissions := []AppPermission{} + findPermissionsResult := svc.db.Find(&appPermissions, &AppPermission{ + AppId: app.ID, + }) + if findPermissionsResult.RowsAffected == 0 { + // No permissions created for this app. It can do anything + return strings.Split(NIP_47_CAPABILITIES, ",") + } + requestMethods := make([]string, 0, len(appPermissions)) + for _, appPermission := range appPermissions { + requestMethods = append(requestMethods, appPermission.RequestMethod) + } + return requestMethods +} + func (svc *Service) hasPermission(app *App, event *nostr.Event, requestMethod string, paymentRequest *decodepay.Bolt11) (result bool, code string, message string) { // find all permissions for the app appPermissions := []AppPermission{} From 56fc1bb7c34210bb15b8d18e120ab716a7b8229a Mon Sep 17 00:00:00 2001 From: Roland Bewick Date: Tue, 12 Dec 2023 15:30:57 +0700 Subject: [PATCH 07/17] chore: remove broken alby http request for get_info --- alby.go | 101 +++++++++++++++++++++++++++----------------------------- 1 file changed, 48 insertions(+), 53 deletions(-) diff --git a/alby.go b/alby.go index 3d4064d3..7542ccd8 100644 --- a/alby.go +++ b/alby.go @@ -281,60 +281,55 @@ func (svc *AlbyOAuthService) GetInfo(ctx context.Context, senderPubkey string) ( }).Errorf("App not found: %v", err) return nil, err } - tok, err := svc.FetchUserToken(ctx, app) - if err != nil { - return nil, err - } - client := svc.oauthConf.Client(ctx, tok) - - req, err := http.NewRequest("GET", fmt.Sprintf("%s/getinfo", svc.cfg.AlbyAPIURL), nil) - if err != nil { - svc.Logger.WithError(err).Error("Error creating request /getinfo") - return nil, err - } - - req.Header.Set("User-Agent", "NWC") - - resp, err := client.Do(req) - if err != nil { - svc.Logger.WithFields(logrus.Fields{ - "senderPubkey": senderPubkey, - "appId": app.ID, - "userId": app.User.ID, - }).Errorf("Failed to fetch node info: %v", err) - return nil, err - } - - if resp.StatusCode < 300 { - // TODO: decode fetched node info - // err = json.NewDecoder(resp.Body).Decode(&nodeInfo) - if err != nil { - return nil, err - } - svc.Logger.WithFields(logrus.Fields{ - "senderPubkey": senderPubkey, - "appId": app.ID, - "userId": app.User.ID, - }).Info("Info fetch successful") - return &NodeInfo{ - Alias: alias, - Color: "color", - Pubkey: "pubkey", - Network: "mainnet", - BlockHeight: 0, - BlockHash: "blockhash", - }, err - } - - errorPayload := &ErrorResponse{} - err = json.NewDecoder(resp.Body).Decode(errorPayload) + // tok, err := svc.FetchUserToken(ctx, app) + // if err != nil { + // return nil, err + // } + //client := svc.oauthConf.Client(ctx, tok) + + // req, err := http.NewRequest("GET", fmt.Sprintf("%s/getinfo", svc.cfg.AlbyAPIURL), nil) + // if err != nil { + // svc.Logger.WithError(err).Error("Error creating request /getinfo") + // return nil, err + // } + + // req.Header.Set("User-Agent", "NWC") + + // resp, err := client.Do(req) + // if err != nil { + // svc.Logger.WithFields(logrus.Fields{ + // "senderPubkey": senderPubkey, + // "appId": app.ID, + // "userId": app.User.ID, + // }).Errorf("Failed to fetch node info: %v", err) + // return nil, err + // } + + // if resp.StatusCode < 300 { svc.Logger.WithFields(logrus.Fields{ - "senderPubkey": senderPubkey, - "appId": app.ID, - "userId": app.User.ID, - "APIHttpStatus": resp.StatusCode, - }).Errorf("Invoices listing failed %s", string(errorPayload.Message)) - return nil, errors.New(errorPayload.Message) + "senderPubkey": senderPubkey, + "appId": app.ID, + "userId": app.User.ID, + }).Info("Info fetch successful") + return &NodeInfo{ + Alias: alias, + Color: "", + Pubkey: "", + Network: "mainnet", + BlockHeight: 0, + BlockHash: "", + }, err + // } + + // errorPayload := &ErrorResponse{} + // err = json.NewDecoder(resp.Body).Decode(errorPayload) + // svc.Logger.WithFields(logrus.Fields{ + // "senderPubkey": senderPubkey, + // "appId": app.ID, + // "userId": app.User.ID, + // // "APIHttpStatus": resp.StatusCode, + // }).Errorf("Invoices listing failed %s", string(errorPayload.Message)) + // return nil, errors.New(errorPayload.Message) } func (svc *AlbyOAuthService) GetBalance(ctx context.Context, senderPubkey string) (balance int64, err error) { From effd82f907eae454df75efdbdf46b723b09f3359 Mon Sep 17 00:00:00 2001 From: Roland Bewick Date: Tue, 12 Dec 2023 15:31:12 +0700 Subject: [PATCH 08/17] chore: set golang.go as default formatter for vscode --- .vscode/settings.json | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..ca1751b9 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "editor.defaultFormatter": "golang.go" +} \ No newline at end of file From 8ea9c87e768d8f1c35bed84944ec7ac6301cc0c0 Mon Sep 17 00:00:00 2001 From: Roland Bewick Date: Tue, 12 Dec 2023 19:06:06 +0700 Subject: [PATCH 09/17] chore: add polar example to .env.example --- .env.example | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/.env.example b/.env.example index db1e3049..e0e7c094 100644 --- a/.env.example +++ b/.env.example @@ -1,10 +1,18 @@ DATABASE_URI=file:nwc.db NOSTR_PRIVKEY= -LN_BACKEND_TYPE=ALBY -ALBY_CLIENT_SECRET= -ALBY_CLIENT_ID= -OAUTH_REDIRECT_URL=http://localhost:8080/alby/callback COOKIE_SECRET=secretsecret RELAY=wss://relay.getalby.com/v1 PUBLIC_RELAY= PORT=8080 + +# Polar LND Client +#LN_BACKEND_TYPE=LND +#LND_CERT_FILE=/home/YOUR_USERNAME/.polar/networks/1/volumes/lnd/alice/tls.cert +#LND_ADDRESS=127.0.0.1:10001 +#LND_MACAROON_FILE=/home/YOUR_USERNAME/.polar/networks/1/volumes/lnd/alice/data/chain/bitcoin/regtest/admin.macaroon + +# Alby Wallet API Client +#LN_BACKEND_TYPE=ALBY +#ALBY_CLIENT_SECRET= +#ALBY_CLIENT_ID= +#OAUTH_REDIRECT_URL=http://localhost:8080/alby/callback \ No newline at end of file From 8905d0011d930a0c14054f3db82985ab3007654f Mon Sep 17 00:00:00 2001 From: Roland Bewick Date: Tue, 12 Dec 2023 19:06:39 +0700 Subject: [PATCH 10/17] doc: update supported methods in README --- README.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/README.md b/README.md index 28b1266c..ba24cbac 100644 --- a/README.md +++ b/README.md @@ -132,8 +132,6 @@ You can also contribute to our [bounty program](https://github.com/getAlby/light ✅ `pay_invoice` ⚠️ `make_invoice` -- ⚠️ preimage in response not supported (TBC) -- ⚠️ preimage in response not supported (TBC) - ⚠️ invoice in response missing (TODO) ⚠️ `lookup_invoice` @@ -167,8 +165,6 @@ You can also contribute to our [bounty program](https://github.com/getAlby/light ⚠️ `make_invoice` - ⚠️ expiry in request not supported -- ⚠️ preimage in request not supported (TBC) -- ⚠️ preimage in response not supported (TBC) - ⚠️ invoice in response missing (TODO) ⚠️ `lookup_invoice` From 192baf8cc4ee47b37c53b10990863110849bc178 Mon Sep 17 00:00:00 2001 From: Roland Bewick Date: Tue, 12 Dec 2023 19:12:57 +0700 Subject: [PATCH 11/17] fix: remove unnecessary property in NodeInfo --- models.go | 1 - 1 file changed, 1 deletion(-) diff --git a/models.go b/models.go index 6e30d3aa..74feaa6f 100644 --- a/models.go +++ b/models.go @@ -164,7 +164,6 @@ type NodeInfo struct { Network string BlockHeight uint32 BlockHash string - Methods []string } type Identity struct { From 75e82d24ce0028cd9dd852667dc7d4ed0b90c188 Mon Sep 17 00:00:00 2001 From: Roland Bewick Date: Tue, 12 Dec 2023 19:20:24 +0700 Subject: [PATCH 12/17] chore: add TODOs to move models --- models.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/models.go b/models.go index 74feaa6f..c1cc07c2 100644 --- a/models.go +++ b/models.go @@ -51,6 +51,7 @@ var nip47MethodIcons = map[string]string{ NIP_47_LOOKUP_INVOICE_METHOD: "search", } +// TODO: move to models/Alby type AlbyMe struct { Identifier string `json:"identifier"` NPub string `json:"nostr_pubkey"` @@ -124,6 +125,7 @@ type PayRequest struct { Invoice string `json:"invoice"` } +// TODO: move to models/Alby type BalanceResponse struct { Balance int64 `json:"balance"` Currency string `json:"currency"` @@ -157,6 +159,7 @@ type ErrorResponse struct { Message string `json:"message"` } +// TODO: move to models/LNClient type NodeInfo struct { Alias string Color string @@ -199,6 +202,7 @@ type Nip47BalanceResponse struct { BudgetRenewal string `json:"budget_renewal"` } +// TODO: move to models/Nip47 type Nip47GetInfoResponse struct { Alias string `json:"alias"` Color string `json:"color"` From 0f2e9f14d10e6eb0032303c57cc6e944a539160e Mon Sep 17 00:00:00 2001 From: Roland Bewick Date: Tue, 12 Dec 2023 19:20:34 +0700 Subject: [PATCH 13/17] chore: remove commented code --- alby.go | 40 ++-------------------------------------- 1 file changed, 2 insertions(+), 38 deletions(-) diff --git a/alby.go b/alby.go index 7542ccd8..18c7c583 100644 --- a/alby.go +++ b/alby.go @@ -270,7 +270,6 @@ func (svc *AlbyOAuthService) LookupInvoice(ctx context.Context, senderPubkey str } func (svc *AlbyOAuthService) GetInfo(ctx context.Context, senderPubkey string) (info *NodeInfo, err error) { - alias := "getalby.com" app := App{} err = svc.db.Preload("User").First(&app, &App{ NostrPubkey: senderPubkey, @@ -281,55 +280,20 @@ func (svc *AlbyOAuthService) GetInfo(ctx context.Context, senderPubkey string) ( }).Errorf("App not found: %v", err) return nil, err } - // tok, err := svc.FetchUserToken(ctx, app) - // if err != nil { - // return nil, err - // } - //client := svc.oauthConf.Client(ctx, tok) - - // req, err := http.NewRequest("GET", fmt.Sprintf("%s/getinfo", svc.cfg.AlbyAPIURL), nil) - // if err != nil { - // svc.Logger.WithError(err).Error("Error creating request /getinfo") - // return nil, err - // } - - // req.Header.Set("User-Agent", "NWC") - - // resp, err := client.Do(req) - // if err != nil { - // svc.Logger.WithFields(logrus.Fields{ - // "senderPubkey": senderPubkey, - // "appId": app.ID, - // "userId": app.User.ID, - // }).Errorf("Failed to fetch node info: %v", err) - // return nil, err - // } - - // if resp.StatusCode < 300 { + svc.Logger.WithFields(logrus.Fields{ "senderPubkey": senderPubkey, "appId": app.ID, "userId": app.User.ID, }).Info("Info fetch successful") return &NodeInfo{ - Alias: alias, + Alias: "getalby.com", Color: "", Pubkey: "", Network: "mainnet", BlockHeight: 0, BlockHash: "", }, err - // } - - // errorPayload := &ErrorResponse{} - // err = json.NewDecoder(resp.Body).Decode(errorPayload) - // svc.Logger.WithFields(logrus.Fields{ - // "senderPubkey": senderPubkey, - // "appId": app.ID, - // "userId": app.User.ID, - // // "APIHttpStatus": resp.StatusCode, - // }).Errorf("Invoices listing failed %s", string(errorPayload.Message)) - // return nil, errors.New(errorPayload.Message) } func (svc *AlbyOAuthService) GetBalance(ctx context.Context, senderPubkey string) (balance int64, err error) { From ef6cbc289e643d2d26d2e284ffa55e928e13f329 Mon Sep 17 00:00:00 2001 From: Roland Bewick Date: Tue, 12 Dec 2023 21:21:57 +0700 Subject: [PATCH 14/17] chore: add get_info test --- README.md | 8 ------ service_test.go | 72 ++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 59 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index ba24cbac..628ae67b 100644 --- a/README.md +++ b/README.md @@ -142,10 +142,6 @@ You can also contribute to our [bounty program](https://github.com/getAlby/light ❌ `list_transactions` -❌ `list_invoices` - -❌ `list_payments` - ❌ `multi_pay_invoice (TBC)` ❌ `multi_pay_keysend (TBC)` @@ -175,10 +171,6 @@ You can also contribute to our [bounty program](https://github.com/getAlby/light ❌ `list_transactions` -❌ `list_invoices` - -❌ `list_payments` - ❌ `multi_pay_invoice (TBC)` ❌ `multi_pay_keysend (TBC)` diff --git a/service_test.go b/service_test.go index 6a36c836..be6ae159 100644 --- a/service_test.go +++ b/service_test.go @@ -22,6 +22,13 @@ const nip47GetBalanceJson = ` "method": "get_balance" } ` + +const nip47GetInfoJson = ` +{ + "method": "get_info" +} +` + const nip47MakeInvoiceJson = ` { "method": "make_invoice", @@ -68,6 +75,7 @@ const nip47PayJsonNoInvoice = ` const mockInvoice = "lnbc10n1pjdy9aepp5ftvu6fucndg5mp5ww4ghsduqrxgr4rtcwelrln4jzxhem5qw022qhp50kncf9zk35xg4lxewt4974ry6mudygsztsz8qn3ar8pn3mtpe50scqzzsxqyz5vqsp5zyzp3dyn98g7sjlgy4nvujq3rh9xxsagytcyx050mf3rtrx3sn4s9qyyssq7af24ljqf5uzgnz4ualxhvffryh3kpkvvj76ah9yrgdvu494lmfrdf36h5wuhshzemkvrtg2zu70uk0fd9fcmxgl3j9748dvvx9ey9gqpr4jjd" const mockPaymentHash = "4ad9cd27989b514d868e755178378019903a8d78767e3fceb211af9dd00e7a94" // for the above invoice +// TODO: split up into individual tests func TestHandleEvent(t *testing.T) { ctx := context.TODO() svc, _ := createTestService(t) @@ -331,14 +339,6 @@ func TestHandleEvent(t *testing.T) { // make invoice: with permission err = svc.db.Model(&AppPermission{}).Where("app_id = ?", app.ID).Update("request_method", NIP_47_MAKE_INVOICE_METHOD).Error - assert.NoError(t, err) - appPermission = &AppPermission{ - AppId: app.ID, - App: app, - RequestMethod: NIP_47_MAKE_INVOICE_METHOD, - ExpiresAt: expiresAt, - } - err = svc.db.Create(appPermission).Error res, err = svc.HandleEvent(ctx, &nostr.Event{ ID: "test_event_13", Kind: NIP_47_REQUEST_KIND, @@ -379,15 +379,56 @@ func TestHandleEvent(t *testing.T) { // lookup invoice: with permission err = svc.db.Model(&AppPermission{}).Where("app_id = ?", app.ID).Update("request_method", NIP_47_LOOKUP_INVOICE_METHOD).Error assert.NoError(t, err) + res, err = svc.HandleEvent(ctx, &nostr.Event{ + ID: "test_event_15", + Kind: NIP_47_REQUEST_KIND, + PubKey: senderPubkey, + Content: newPayload, + }) + assert.NoError(t, err) + assert.NotNil(t, res) + decrypted, err = nip04.Decrypt(res.Content, ss) + assert.NoError(t, err) + received = &Nip47Response{ + Result: &Nip47LookupInvoiceResponse{}, + } + err = json.Unmarshal([]byte(decrypted), received) + assert.NoError(t, err) + assert.Equal(t, mockInvoice, received.Result.(*Nip47LookupInvoiceResponse).Invoice) + assert.Equal(t, false, received.Result.(*Nip47LookupInvoiceResponse).Paid) + + // get info: without permission + newPayload, err = nip04.Encrypt(nip47GetInfoJson, ss) + assert.NoError(t, err) + res, err = svc.HandleEvent(ctx, &nostr.Event{ + ID: "test_event_16", + Kind: NIP_47_REQUEST_KIND, + PubKey: senderPubkey, + Content: newPayload, + }) + assert.NoError(t, err) + assert.NotNil(t, res) + decrypted, err = nip04.Decrypt(res.Content, ss) + assert.NoError(t, err) + received = &Nip47Response{} + err = json.Unmarshal([]byte(decrypted), received) + assert.NoError(t, err) + assert.Equal(t, NIP_47_ERROR_RESTRICTED, received.Error.Code) + assert.NotNil(t, res) + + // delete all permissions + svc.db.Exec("delete from app_permissions") + + // lookup invoice: with permission appPermission = &AppPermission{ AppId: app.ID, App: app, - RequestMethod: NIP_47_LOOKUP_INVOICE_METHOD, + RequestMethod: NIP_47_GET_INFO_METHOD, ExpiresAt: expiresAt, } err = svc.db.Create(appPermission).Error res, err = svc.HandleEvent(ctx, &nostr.Event{ - ID: "test_event_15", + ID: "test_event_17", Kind: NIP_47_REQUEST_KIND, PubKey: senderPubkey, Content: newPayload, @@ -397,12 +438,17 @@ func TestHandleEvent(t *testing.T) { decrypted, err = nip04.Decrypt(res.Content, ss) assert.NoError(t, err) received = &Nip47Response{ - Result: &Nip47LookupInvoiceResponse{}, + Result: &Nip47GetInfoResponse{}, } err = json.Unmarshal([]byte(decrypted), received) assert.NoError(t, err) - assert.Equal(t, mockInvoice, received.Result.(*Nip47LookupInvoiceResponse).Invoice) - assert.Equal(t, false, received.Result.(*Nip47LookupInvoiceResponse).Paid) + assert.Equal(t, "bob", received.Result.(*Nip47GetInfoResponse).Alias) + assert.Equal(t, "#3399FF", received.Result.(*Nip47GetInfoResponse).Color) + assert.Equal(t, "123pubkey", received.Result.(*Nip47GetInfoResponse).Pubkey) + assert.Equal(t, "testnet", received.Result.(*Nip47GetInfoResponse).Network) + assert.Equal(t, uint32(12), received.Result.(*Nip47GetInfoResponse).BlockHeight) + assert.Equal(t, "123blockhash", received.Result.(*Nip47GetInfoResponse).BlockHash) + assert.Equal(t, []string{"get_info"}, received.Result.(*Nip47GetInfoResponse).Methods) } func createTestService(t *testing.T) (svc *Service, ln *MockLn) { From 841eb53123990d2558d9346752de8e425fd8f75e Mon Sep 17 00:00:00 2001 From: Roland Bewick Date: Tue, 12 Dec 2023 21:25:02 +0700 Subject: [PATCH 15/17] chore: add TODO --- alby.go | 1 + 1 file changed, 1 insertion(+) diff --git a/alby.go b/alby.go index 18c7c583..874748fe 100644 --- a/alby.go +++ b/alby.go @@ -139,6 +139,7 @@ func (svc *AlbyOAuthService) MakeInvoice(ctx context.Context, senderPubkey strin return "", "", err } + // TODO: move to creation of HTTP client req.Header.Set("User-Agent", "NWC") req.Header.Set("Content-Type", "application/json") From c50795e934eeb730fe2a1c6813821ee48f4288cf Mon Sep 17 00:00:00 2001 From: Roland Bewick Date: Wed, 13 Dec 2023 12:59:49 +0700 Subject: [PATCH 16/17] chore: extract mock node info from tests to remove duplication --- service_test.go | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/service_test.go b/service_test.go index be6ae159..877e40b5 100644 --- a/service_test.go +++ b/service_test.go @@ -74,6 +74,14 @@ const nip47PayJsonNoInvoice = ` const mockInvoice = "lnbc10n1pjdy9aepp5ftvu6fucndg5mp5ww4ghsduqrxgr4rtcwelrln4jzxhem5qw022qhp50kncf9zk35xg4lxewt4974ry6mudygsztsz8qn3ar8pn3mtpe50scqzzsxqyz5vqsp5zyzp3dyn98g7sjlgy4nvujq3rh9xxsagytcyx050mf3rtrx3sn4s9qyyssq7af24ljqf5uzgnz4ualxhvffryh3kpkvvj76ah9yrgdvu494lmfrdf36h5wuhshzemkvrtg2zu70uk0fd9fcmxgl3j9748dvvx9ey9gqpr4jjd" const mockPaymentHash = "4ad9cd27989b514d868e755178378019903a8d78767e3fceb211af9dd00e7a94" // for the above invoice +var mockNodeInfo = NodeInfo{ + Alias: "bob", + Color: "#3399FF", + Pubkey: "123pubkey", + Network: "testnet", + BlockHeight: 12, + BlockHash: "123blockhash", +} // TODO: split up into individual tests func TestHandleEvent(t *testing.T) { @@ -442,12 +450,12 @@ func TestHandleEvent(t *testing.T) { } err = json.Unmarshal([]byte(decrypted), received) assert.NoError(t, err) - assert.Equal(t, "bob", received.Result.(*Nip47GetInfoResponse).Alias) - assert.Equal(t, "#3399FF", received.Result.(*Nip47GetInfoResponse).Color) - assert.Equal(t, "123pubkey", received.Result.(*Nip47GetInfoResponse).Pubkey) - assert.Equal(t, "testnet", received.Result.(*Nip47GetInfoResponse).Network) - assert.Equal(t, uint32(12), received.Result.(*Nip47GetInfoResponse).BlockHeight) - assert.Equal(t, "123blockhash", received.Result.(*Nip47GetInfoResponse).BlockHash) + assert.Equal(t, mockNodeInfo.Alias, received.Result.(*Nip47GetInfoResponse).Alias) + assert.Equal(t, mockNodeInfo.Color, received.Result.(*Nip47GetInfoResponse).Color) + assert.Equal(t, mockNodeInfo.Pubkey, received.Result.(*Nip47GetInfoResponse).Pubkey) + assert.Equal(t, mockNodeInfo.Network, received.Result.(*Nip47GetInfoResponse).Network) + assert.Equal(t, mockNodeInfo.BlockHeight, received.Result.(*Nip47GetInfoResponse).BlockHeight) + assert.Equal(t, mockNodeInfo.BlockHash, received.Result.(*Nip47GetInfoResponse).BlockHash) assert.Equal(t, []string{"get_info"}, received.Result.(*Nip47GetInfoResponse).Methods) } @@ -491,14 +499,7 @@ func (mln *MockLn) GetBalance(ctx context.Context, senderPubkey string) (balance } func (mln *MockLn) GetInfo(ctx context.Context, senderPubkey string) (info *NodeInfo, err error) { - return &NodeInfo{ - Alias: "bob", - Color: "#3399FF", - Pubkey: "123pubkey", - Network: "testnet", - BlockHeight: 12, - BlockHash: "123blockhash", - }, nil + return &mockNodeInfo, nil } func (mln *MockLn) MakeInvoice(ctx context.Context, senderPubkey string, amount int64, description string, descriptionHash string, expiry int64) (invoice string, paymentHash string, err error) { From d751b0333d133ff0d91f5f3a21d78f2ce21c5423 Mon Sep 17 00:00:00 2001 From: Roland Bewick Date: Wed, 13 Dec 2023 13:18:25 +0700 Subject: [PATCH 17/17] feat: use network from LND response --- lnd.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lnd.go b/lnd.go index e21e4db6..f0855e32 100644 --- a/lnd.go +++ b/lnd.go @@ -61,7 +61,7 @@ func (svc *LNDService) GetInfo(ctx context.Context, senderPubkey string) (info * Alias: resp.Alias, Color: resp.Color, Pubkey: resp.IdentityPubkey, - Network: "mainnet", + Network: resp.Chains[0].Network, BlockHeight: resp.BlockHeight, BlockHash: resp.BlockHash, }, nil