From a909264d02462c9a80bd95192f203b186f37dee9 Mon Sep 17 00:00:00 2001 From: "Dr. Tobias Baur" Date: Thu, 2 Mar 2023 15:13:48 +0100 Subject: [PATCH] Cleanup, Threading, minor fixes --- lnurl.go | 53 ++++++++++++++++++++++++++---------- waitforinvoice.go | 69 ++++++++++++++++++++++++----------------------- 2 files changed, 74 insertions(+), 48 deletions(-) diff --git a/lnurl.go b/lnurl.go index 4130bab..4112053 100644 --- a/lnurl.go +++ b/lnurl.go @@ -11,6 +11,7 @@ import ( "github.com/fiatjaf/go-lnurl" "github.com/gorilla/mux" "github.com/nbd-wtf/go-nostr" + decodepay "github.com/nbd-wtf/ln-decodepay" ) var allowNostr bool = false @@ -34,6 +35,19 @@ type LNURLPayParamsCustom struct { Metadata lnurl.Metadata `json:"-"` } +type LNURLPayValuesCustom struct { + lnurl.LNURLResponse + SuccessAction *lnurl.SuccessAction `json:"successAction"` + Routes interface{} `json:"routes"` // ignored + PR string `json:"pr"` + Disposable *bool `json:"disposable,omitempty"` + + ParsedInvoice decodepay.Bolt11 `json:"-"` + PayerDataJSON string `json:"-"` + nip57Receipt nostr.Event `json:"nip57Receipt"` + nip57ReceiptRelays []string `json:"nip57ReceiptRelays"` +} + func handleLNURL(w http.ResponseWriter, r *http.Request) { var err error var response interface{} @@ -160,7 +174,8 @@ func handleLNURL(w http.ResponseWriter, r *http.Request) { //we outsource the second part in a function, we should do this for the first one too. response, err = serveLNURLpSecond(w, params, username, msat, comment, payerData, zapEvent) - var payvalues = response.(*lnurl.LNURLPayValues) + var payvaluescustom = response.(LNURLPayValuesCustom) + //var payvalues = response.(*lnurl.LNURLPayValues) if err != nil { if response != nil { // there is a valid error response @@ -168,29 +183,36 @@ func handleLNURL(w http.ResponseWriter, r *http.Request) { } return } - err = json.NewEncoder(w).Encode(response) + + json.NewEncoder(w).Encode(lnurl.LNURLPayValues{ + LNURLResponse: payvaluescustom.LNURLResponse, + PR: payvaluescustom.PR, + Routes: payvaluescustom.Routes, + SuccessAction: payvaluescustom.SuccessAction, + }) + + //err = json.NewEncoder(w).Encode(payvalues) if err != nil { json.NewEncoder(w).Encode(response) return } if allowNostr { - WaitForInvoicePaid(payvalues, params) + go WaitForInvoicePaid(payvaluescustom, params) } } } -func serveLNURLpSecond(w http.ResponseWriter, params *Params, username string, amount_msat int, comment string, payerData lnurl.PayerDataValues, zapEvent nostr.Event) (*lnurl.LNURLPayValues, error) { +func serveLNURLpSecond(w http.ResponseWriter, params *Params, username string, amount_msat int, comment string, payerData lnurl.PayerDataValues, zapEvent nostr.Event) (LNURLPayValuesCustom, error) { log.Debug().Any("Serving invoice for user %s", username) if amount_msat < minSendable || amount_msat > maxSendable { // amount is not ok - return &lnurl.LNURLPayValues{ + return LNURLPayValuesCustom{ LNURLResponse: lnurl.LNURLResponse{ Status: "Error", Reason: fmt.Sprintf("Amount out of bounds (min: %d sat, max: %d sat).", minSendable/1000, maxSendable/1000)}, }, fmt.Errorf("amount out of bounds") } - var resp *lnurl.LNURLPayValues // NIP57 ZAPs // for nip57 use the nostr event as the descriptionHash @@ -202,7 +224,7 @@ func serveLNURLpSecond(w http.ResponseWriter, params *Params, username string, a zapEventSerializedStr = fmt.Sprintf("%s", zapEventSerialized) if err != nil { //log.Println(err) - return &lnurl.LNURLPayValues{ + return LNURLPayValuesCustom{ LNURLResponse: lnurl.LNURLResponse{ Status: "Error", Reason: "Couldn't serialize zap event."}, @@ -228,15 +250,16 @@ func serveLNURLpSecond(w http.ResponseWriter, params *Params, username string, a params.zapEventSerializedStr = "" } + var response LNURLPayValuesCustom invoice, err := makeInvoice(params, amount_msat, nil) if err != nil { err = fmt.Errorf("Couldn't create invoice: %v", err.Error()) - resp = &lnurl.LNURLPayValues{ + response = LNURLPayValuesCustom{ LNURLResponse: lnurl.LNURLResponse{ Status: "Error", Reason: "Couldn't create invoice."}, } - return resp, err + return response, err } // nip57 - we need to store the newly created invoice in the zap receipt if zapEvent.Sig != "" { @@ -258,11 +281,13 @@ func serveLNURLpSecond(w http.ResponseWriter, params *Params, username string, a nip57Receipt.Sign(pk) } - return &lnurl.LNURLPayValues{ - LNURLResponse: lnurl.LNURLResponse{Status: "OK"}, - PR: invoice, - Routes: make([]struct{}, 0), - SuccessAction: &lnurl.SuccessAction{Message: "Payment Received!", Tag: "message"}, + return LNURLPayValuesCustom{ + LNURLResponse: lnurl.LNURLResponse{Status: "OK"}, + PR: invoice, + Routes: make([]struct{}, 0), + SuccessAction: &lnurl.SuccessAction{Message: "Payment Received!", Tag: "message"}, + nip57Receipt: nip57Receipt, + nip57ReceiptRelays: nip57ReceiptRelays, }, nil } diff --git a/waitforinvoice.go b/waitforinvoice.go index 87ef472..77e0a51 100644 --- a/waitforinvoice.go +++ b/waitforinvoice.go @@ -14,7 +14,6 @@ import ( "strings" "time" - "github.com/fiatjaf/go-lnurl" decodepay "github.com/nbd-wtf/ln-decodepay" "github.com/tidwall/gjson" ) @@ -45,7 +44,7 @@ type CommandoParams struct { } func (l CommandoParams) getCert() string { return "" } -func (l CommandoParams) isTor() bool { return strings.Index(l.Host, ".onion") != -1 } +func (l CommandoParams) isTor() bool { return strings.Contains(l.Host, ".onion") } type SparkoParams struct { Cert string @@ -54,7 +53,7 @@ type SparkoParams struct { } func (l SparkoParams) getCert() string { return l.Cert } -func (l SparkoParams) isTor() bool { return strings.Index(l.Host, ".onion") != -1 } +func (l SparkoParams) isTor() bool { return strings.Contains(l.Host, ".onion") } type LNDParams struct { Cert string @@ -63,7 +62,7 @@ type LNDParams struct { } func (l LNDParams) getCert() string { return l.Cert } -func (l LNDParams) isTor() bool { return strings.Index(l.Host, ".onion") != -1 } +func (l LNDParams) isTor() bool { return strings.Contains(l.Host, ".onion") } type LNBitsParams struct { Cert string @@ -72,7 +71,7 @@ type LNBitsParams struct { } func (l LNBitsParams) getCert() string { return l.Cert } -func (l LNBitsParams) isTor() bool { return strings.Index(l.Host, ".onion") != -1 } +func (l LNBitsParams) isTor() bool { return strings.Contains(l.Host, ".onion") } type LNPayParams struct { PublicAccessKey string @@ -89,7 +88,7 @@ type EclairParams struct { } func (l EclairParams) getCert() string { return l.Cert } -func (l EclairParams) isTor() bool { return strings.Index(l.Host, ".onion") != -1 } +func (l EclairParams) isTor() bool { return strings.Contains(l.Host, ".onion") } type StrikeParams struct { Key string @@ -105,7 +104,7 @@ type BackendParams interface { isTor() bool } -func WaitForInvoicePaid(payvalues *lnurl.LNURLPayValues, params *Params) { +func WaitForInvoicePaid(payvalues LNURLPayValuesCustom, params *Params) { //Check for a minute if invoice is paid //Do we have an easier way to do this? How does it work for other backends than lnbits. go func() { @@ -182,9 +181,11 @@ func WaitForInvoicePaid(payvalues *lnurl.LNURLPayValues, params *Params) { case <-ticker.C: bolt11, _ := decodepay.Decodepay(payvalues.PR) + var isPaid bool = false switch backend := mip.Backend.(type) { case SparkoParams: + //TODO case LNDParams: req, err := http.NewRequest("GET", backend.Host+"/v1/invoice/"+bolt11.PaymentHash, @@ -196,7 +197,6 @@ func WaitForInvoicePaid(payvalues *lnurl.LNURLPayValues, params *Params) { if b, err := base64.StdEncoding.DecodeString(backend.Macaroon); err == nil { backend.Macaroon = hex.EncodeToString(b) } - req.Header.Set("Grpc-Metadata-macaroon", backend.Macaroon) resp, err := Client.Do(req) if err != nil { @@ -204,14 +204,6 @@ func WaitForInvoicePaid(payvalues *lnurl.LNURLPayValues, params *Params) { return } defer resp.Body.Close() - if resp.StatusCode >= 300 { - body, _ := ioutil.ReadAll(resp.Body) - text := string(body) - if len(text) > 300 { - text = text[:300] - } - return - } b, err := ioutil.ReadAll(resp.Body) if err != nil { @@ -219,11 +211,8 @@ func WaitForInvoicePaid(payvalues *lnurl.LNURLPayValues, params *Params) { return } - if gjson.ParseBytes(b).Get("settled").String() == "true" && bolt11.DescriptionHash == Nip57DescriptionHash(zapEventSerializedStr) { - log.Debug().Str("ZAP", "Published on Nostr").Msg("zapped") - publishNostrEvent(nip57Receipt, nip57ReceiptRelays) - close(quit) - return + if gjson.ParseBytes(b).Get("settled").String() == "true" { + isPaid = true } case LNBitsParams: @@ -240,25 +229,37 @@ func WaitForInvoicePaid(payvalues *lnurl.LNURLPayValues, params *Params) { } var jsonMap map[string]interface{} json.Unmarshal([]byte(string(responseData)), &jsonMap) - //If invoice is paid and DescriptionHash matches Nip57 DescriptionHash, publish Zap Nostr Event - if jsonMap["paid"].(bool) && bolt11.DescriptionHash == Nip57DescriptionHash(zapEventSerializedStr) { + + if jsonMap["paid"].(bool) { + isPaid = true + } + + case LNPayParams: + //TODO + case EclairParams: + //TODO + case CommandoParams: + //TODO + + } + //Timeout waiting for payment after maxiterations + if maxiterations == 0 { + log.Debug().Str("NIP57", bolt11.PaymentHash).Msg("Timed out") + close(quit) + } + + //If invoice is paid and DescriptionHash matches Nip57 DescriptionHash, publish Zap Nostr Event. This is rather a sanity check. + if isPaid { + var descriptionTag = *payvalues.nip57Receipt.Tags.GetFirst([]string{"description"}) + if bolt11.DescriptionHash == Nip57DescriptionHash(descriptionTag.Value()) { log.Debug().Str("ZAP", "Published on Nostr").Msg("zapped") - publishNostrEvent(nip57Receipt, nip57ReceiptRelays) + publishNostrEvent(payvalues.nip57Receipt, payvalues.nip57ReceiptRelays) close(quit) return } - case LNPayParams: - case EclairParams: - case CommandoParams: - + maxiterations-- } - - if maxiterations == 0 { - log.Debug().Str("ZAP", bolt11.PaymentHash).Msg("Timed out") - close(quit) - } - maxiterations-- case <-quit: ticker.Stop() return