Skip to content

Commit

Permalink
feat: add multi keysend method
Browse files Browse the repository at this point in the history
  • Loading branch information
im-adithya committed Jan 25, 2024
1 parent 01786f8 commit 6c2fe34
Show file tree
Hide file tree
Showing 4 changed files with 185 additions and 13 deletions.
14 changes: 7 additions & 7 deletions handle_multi_pay_invoice_request.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ func (svc *Service) HandleMultiPayInvoiceEvent(ctx context.Context, sub *nostr.S
return
}

multiPayParams := &Nip47MultiPayParams{}
multiPayParams := &Nip47MultiPayInvoiceParams{}
err = json.Unmarshal(request.Params, multiPayParams)
if err != nil {
svc.Logger.WithFields(logrus.Fields{
Expand Down Expand Up @@ -64,7 +64,7 @@ func (svc *Service) HandleMultiPayInvoiceEvent(ctx context.Context, sub *nostr.S
// TODO: Decide what to do if id is empty
dTag := []string{"d", invoiceInfo.Id}
resp, err := svc.createResponse(event, Nip47Response{
ResultType: NIP_47_MULTI_PAY_INVOICE_METHOD,
ResultType: request.Method,
Error: &Nip47Error{
Code: NIP_47_ERROR_INTERNAL,
Message: fmt.Sprintf("Failed to decode bolt11 invoice: %s", err.Error()),
Expand Down Expand Up @@ -100,7 +100,7 @@ func (svc *Service) HandleMultiPayInvoiceEvent(ctx context.Context, sub *nostr.S
}).Errorf("App does not have permission: %s %s", code, message)

resp, err := svc.createResponse(event, Nip47Response{
ResultType: NIP_47_MULTI_PAY_INVOICE_METHOD,
ResultType: request.Method,
Error: &Nip47Error{
Code: code,
Message: message,
Expand Down Expand Up @@ -146,12 +146,12 @@ func (svc *Service) HandleMultiPayInvoiceEvent(ctx context.Context, sub *nostr.S
"appId": app.ID,
"bolt11": bolt11,
}).Infof("Failed to send payment: %v", err)
// TODO: What to do here?
// TODO: https://github.com/getAlby/nostr-wallet-connect/issues/231
nostrEvent.State = NOSTR_EVENT_STATE_HANDLER_ERROR
svc.db.Save(&nostrEvent)

resp, err := svc.createResponse(event, Nip47Response{
ResultType: NIP_47_MULTI_PAY_INVOICE_METHOD,
ResultType: request.Method,
Error: &Nip47Error{
Code: NIP_47_ERROR_INTERNAL,
Message: fmt.Sprintf("Something went wrong while paying invoice: %s", err.Error()),
Expand All @@ -170,12 +170,12 @@ func (svc *Service) HandleMultiPayInvoiceEvent(ctx context.Context, sub *nostr.S
return
}
payment.Preimage = &preimage
// TODO: What to do here?
// TODO: https://github.com/getAlby/nostr-wallet-connect/issues/231
nostrEvent.State = NOSTR_EVENT_STATE_HANDLER_EXECUTED
svc.db.Save(&nostrEvent)
svc.db.Save(&payment)
resp, err := svc.createResponse(event, Nip47Response{
ResultType: NIP_47_MULTI_PAY_INVOICE_METHOD,
ResultType: request.Method,
Result: Nip47PayResponse{
Preimage: preimage,
},
Expand Down
163 changes: 163 additions & 0 deletions handle_multi_pay_keysend_request.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
package main

import (
"context"
"encoding/json"
"fmt"
"sync"

"github.com/nbd-wtf/go-nostr"
"github.com/sirupsen/logrus"
)

func (svc *Service) HandleMultiPayKeysendEvent(ctx context.Context, sub *nostr.Subscription, request *Nip47Request, event *nostr.Event, app App, ss []byte) {

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)
svc.Logger.WithFields(logrus.Fields{
"eventId": event.ID,
"eventKind": event.Kind,
}).Errorf("Failed to process event: %v", err)
return
}

multiPayParams := &Nip47MultiPayKeysendParams{}
err = json.Unmarshal(request.Params, multiPayParams)
if err != nil {
svc.Logger.WithFields(logrus.Fields{
"eventId": event.ID,
"eventKind": event.Kind,
"appId": app.ID,
}).Errorf("Failed to decode nostr event: %v", err)
svc.Logger.WithFields(logrus.Fields{
"eventId": event.ID,
"eventKind": event.Kind,
}).Errorf("Failed to process event: %v", err)
return
}

var wg sync.WaitGroup
for _, keysendInfo := range multiPayParams.Invoices {
wg.Add(1)
go func(keysendInfo Nip47MultiPayKeysendElement) {
defer wg.Done()

keysendDTagValue := keysendInfo.Id
if keysendDTagValue == "" {
keysendDTagValue = keysendInfo.Pubkey
}
dTag := []string{"d", keysendDTagValue}

hasPermission, code, message := svc.hasPermission(&app, event, NIP_47_PAY_INVOICE_METHOD, keysendInfo.Amount)

if !hasPermission {
svc.Logger.WithFields(logrus.Fields{
"eventId": event.ID,
"eventKind": event.Kind,
"appId": app.ID,
"senderPubkey": keysendInfo.Pubkey,
}).Errorf("App does not have permission: %s %s", code, message)

resp, err := svc.createResponse(event, Nip47Response{
ResultType: request.Method,
Error: &Nip47Error{
Code: code,
Message: message,
},
}, nostr.Tags{dTag}, ss)
if err != nil {
svc.Logger.WithFields(logrus.Fields{
"eventId": event.ID,
"eventKind": event.Kind,
"senderPubkey": keysendInfo.Pubkey,
"keysendId": keysendInfo.Id,
}).Errorf("Failed to process event: %v", err)
return
}
svc.PublishEvent(ctx, sub, event, resp)
return
}

payment := Payment{App: app, NostrEvent: nostrEvent, Amount: uint(keysendInfo.Amount / 1000)}
insertPaymentResult := svc.db.Create(&payment)
if insertPaymentResult.Error != nil {
svc.Logger.WithFields(logrus.Fields{
"eventId": event.ID,
"eventKind": event.Kind,
"senderPubkey": keysendInfo.Pubkey,
"keysendId": keysendInfo.Id,
}).Errorf("Failed to process event: %v", insertPaymentResult.Error)
return
}

svc.Logger.WithFields(logrus.Fields{
"eventId": event.ID,
"eventKind": event.Kind,
"appId": app.ID,
"senderPubkey": keysendInfo.Pubkey,
}).Info("Sending payment")

preimage, err := svc.lnClient.SendKeysend(ctx, event.PubKey, keysendInfo.Amount/1000, keysendInfo.Pubkey, keysendInfo.Preimage, keysendInfo.TLVRecords)
if err != nil {
svc.Logger.WithFields(logrus.Fields{
"eventId": event.ID,
"eventKind": event.Kind,
"appId": app.ID,
"senderPubkey": keysendInfo.Pubkey,
}).Infof("Failed to send payment: %v", err)
// TODO: https://github.com/getAlby/nostr-wallet-connect/issues/231
nostrEvent.State = NOSTR_EVENT_STATE_HANDLER_ERROR
svc.db.Save(&nostrEvent)

resp, err := svc.createResponse(event, Nip47Response{
ResultType: request.Method,
Error: &Nip47Error{
Code: NIP_47_ERROR_INTERNAL,
Message: fmt.Sprintf("Something went wrong while paying invoice: %s", err.Error()),
},
}, nostr.Tags{dTag}, ss)
if err != nil {
svc.Logger.WithFields(logrus.Fields{
"eventId": event.ID,
"eventKind": event.Kind,
"senderPubkey": keysendInfo.Pubkey,
"keysendId": keysendInfo.Id,
}).Errorf("Failed to process event: %v", err)
return
}
svc.PublishEvent(ctx, sub, event, resp)
return
}
payment.Preimage = &preimage
// TODO: https://github.com/getAlby/nostr-wallet-connect/issues/231
nostrEvent.State = NOSTR_EVENT_STATE_HANDLER_EXECUTED
svc.db.Save(&nostrEvent)
svc.db.Save(&payment)
resp, err := svc.createResponse(event, Nip47Response{
ResultType: request.Method,
Result: Nip47PayResponse{
Preimage: preimage,
},
}, nostr.Tags{dTag}, ss)
if err != nil {
svc.Logger.WithFields(logrus.Fields{
"eventId": event.ID,
"eventKind": event.Kind,
"senderPubkey": keysendInfo.Pubkey,
"keysendId": keysendInfo.Id,
}).Errorf("Failed to process event: %v", err)
return
}
svc.PublishEvent(ctx, sub, event, resp)
}(keysendInfo)
}

wg.Wait()
return
}
18 changes: 12 additions & 6 deletions models.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ const (
NIP_47_LIST_TRANSACTIONS_METHOD = "list_transactions"
NIP_47_PAY_KEYSEND_METHOD = "pay_keysend"
NIP_47_MULTI_PAY_INVOICE_METHOD = "multi_pay_invoice"
NIP_47_MULTI_PAY_KEYSEND_METHOD = "multi_pay_keysend"
NIP_47_ERROR_INTERNAL = "INTERNAL"
NIP_47_ERROR_NOT_IMPLEMENTED = "NOT_IMPLEMENTED"
NIP_47_ERROR_QUOTA_EXCEEDED = "QUOTA_EXCEEDED"
Expand All @@ -25,7 +26,7 @@ const (
NIP_47_ERROR_EXPIRED = "EXPIRED"
NIP_47_ERROR_RESTRICTED = "RESTRICTED"
NIP_47_OTHER = "OTHER"
NIP_47_CAPABILITIES = "pay_invoice,pay_keysend,get_balance,get_info,make_invoice,lookup_invoice,list_transactions,multi_pay_invoice"
NIP_47_CAPABILITIES = "pay_invoice,pay_keysend,get_balance,get_info,make_invoice,lookup_invoice,list_transactions,multi_pay_invoice,multi_pay_keysend"
)

const (
Expand Down Expand Up @@ -173,7 +174,16 @@ type Nip47PayResponse struct {
Preimage string `json:"preimage"`
}

type Nip47MultiPayParams struct {
type Nip47MultiPayKeysendParams struct {
Invoices []Nip47MultiPayKeysendElement `json:"invoices"`
}

type Nip47MultiPayKeysendElement struct {
Nip47KeysendParams
Id string `json:"id"`
}

type Nip47MultiPayInvoiceParams struct {
Invoices []Nip47MultiPayInvoiceElement `json:"invoices"`
}

Expand All @@ -182,10 +192,6 @@ type Nip47MultiPayInvoiceElement struct {
Id string `json:"id"`
}

type Nip47MultiPayResponse struct {
Invoice string `json:"invoice"`
}

type Nip47KeysendParams struct {
Amount int64 `json:"amount"`
Pubkey string `json:"pubkey"`
Expand Down
3 changes: 3 additions & 0 deletions service.go
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,9 @@ func (svc *Service) HandleEvent(ctx context.Context, sub *nostr.Subscription, ev
case NIP_47_MULTI_PAY_INVOICE_METHOD:
svc.HandleMultiPayInvoiceEvent(ctx, sub, nip47Request, event, app, ss)
return
case NIP_47_MULTI_PAY_KEYSEND_METHOD:
svc.HandleMultiPayKeysendEvent(ctx, sub, nip47Request, event, app, ss)
return
// TODO: for the below handlers consider returning
// Nip47Response instead of *nostr.Event
case NIP_47_PAY_INVOICE_METHOD:
Expand Down

0 comments on commit 6c2fe34

Please sign in to comment.