From 6355ce2cc52d70940bbcaa9f608567abfeb1a470 Mon Sep 17 00:00:00 2001 From: Michael Bumann Date: Fri, 27 Aug 2021 11:52:17 +0200 Subject: [PATCH 1/7] Update README.md --- README.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/README.md b/README.md index fdc2b91..4fc8b5a 100644 --- a/README.md +++ b/README.md @@ -84,6 +84,16 @@ All environment variables must be prefixed by `LNME_` use `_` instead of `-` $ LNME_LND_ADDRESS=127.0.0.1:10005 lnme +### LND Permissions + +LnMe needs the following LND permissions: + +* Read/Write permission for invoices +* Write permission for onchain address (if you want to use the onchain option) + +Use the LND [macaroon bakery](http://macaroon-bakery.freedomnode.com/) to create a new macaroon for LnMe. + +To get the HEX versions of the files use `xxd -ps -u -c 100000` e.g. `cat invoice.macaroon | xxd -ps -u -c 100000` ### Deployment @@ -92,6 +102,8 @@ It is the easiest to run LnMe on the same node as LND. But you can run it anywhe #### Heroku One click deployment with Heroku: +You will need your LND address, the LND tls certificate (HEX) and the macaroon (HEX). + [![Deploy on Heroku](https://www.herokucdn.com/deploy/button.svg)](https://heroku.com/deploy?template=https://github.com/bumi/lnme) #### Notes From 71f6c862fab4118ff82754683953c0cfe46949bd Mon Sep 17 00:00:00 2001 From: Michael Bumann Date: Sat, 28 Aug 2021 00:05:07 +0200 Subject: [PATCH 2/7] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 4fc8b5a..d79d859 100644 --- a/README.md +++ b/README.md @@ -106,6 +106,8 @@ You will need your LND address, the LND tls certificate (HEX) and the macaroon ( [![Deploy on Heroku](https://www.herokucdn.com/deploy/button.svg)](https://heroku.com/deploy?template=https://github.com/bumi/lnme) +Here is a [Video Demo of the Heroku deployment](https://www.youtube.com/watch?v=hSFXhnLp_Rc) + #### Notes To run LnMe as systemd service have a look at the [systemd service example config](https://github.com/bumi/lnme/blob/master/examples/lnme.service) From 19210cc3cef73a84a1737b2c41ab23897e82d07e Mon Sep 17 00:00:00 2001 From: Michael Bumann Date: Sat, 28 Aug 2021 00:42:04 +0200 Subject: [PATCH 3/7] Update README.md --- README.md | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index d79d859..445e136 100644 --- a/README.md +++ b/README.md @@ -4,23 +4,17 @@ LnMe is a personal Bitcoin Lightning payment website and payment widget. ![demo](./lnme-demo.gif) -It is a small service written in Go that connects to a [lnd node](https://github.com/lightningnetwork/lnd/blob/master/docs/INSTALL.md) and exposes a simple HTTP JSON API to create and monitor invoices. -It comes with a configurable personal payment website and offers a JavaScript widget to integrate in existing websites. +**See it in action: [ln.michaelbumann.com](https://ln.michaelbumann.com/)** -If [webln](https://github.com/wbobeirne/webln) is available the widget automatically use webln to request the payment; -otherwise an overlay will be shown with the payment request and a QR code. +LnMe focusses on simplicity and ease of deployment. It connects to an existing lightning node and provides a configurable personal payment page and offers a JavaScript widget to integrate into existing websites. -## Motivation - -I wanted a simple way for people to send Lightning payments using my own lightning node. - -BTCPay Server is too big and hard to run for that and I do not need most of its features. +LnMe is one [simple executable](https://github.com/bumi/lnme/releases) file that can be deployed anywhere with no dependencies. (on your own node or for example with [one click on Heroku](#heroku)) ## Installation LnMe connects to your [LND node](https://github.com/lightningnetwork/lnd/blob/master/docs/INSTALL.md), so a running LND node is required. -LnMe can easily run next to LND on the same system. +LnMe can easily run next to LND on the same system or any other hosting provider. 1. Download the latest [release](https://github.com/bumi/lnme/releases) 2. Run `lnme` @@ -182,6 +176,11 @@ lnme.watchPayment().then(invoice => { ``` +## Motivation + +I wanted a simple way for people to send Lightning payments using my own lightning node. +BTCPay Server is too big and hard to run for that and I do not need most of its features. + ## Development Use `go run` to ron the service locally: From 6e4af5610fe7b4c47638a6f2d0e7d664165dc8fb Mon Sep 17 00:00:00 2001 From: Michael Bumann Date: Sat, 28 Aug 2021 22:11:24 +0200 Subject: [PATCH 4/7] Add Lightning Address support This handles the LNURL-pay flow to enable Lightning Address support. It accepts any lightning address name on your domain. e.g. yourname@yourdomain.com --- README.md | 19 ++++++++++++++---- lnme.go | 45 +++++++++++++++++++++++++++++++++++++++++++ lnurl/types.go | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 112 insertions(+), 4 deletions(-) create mode 100644 lnurl/types.go diff --git a/README.md b/README.md index fdc2b91..870a682 100644 --- a/README.md +++ b/README.md @@ -4,10 +4,10 @@ LnMe is a personal Bitcoin Lightning payment website and payment widget. ![demo](./lnme-demo.gif) -It is a small service written in Go that connects to a [lnd node](https://github.com/lightningnetwork/lnd/blob/master/docs/INSTALL.md) and exposes a simple HTTP JSON API to create and monitor invoices. +It is a small service written in Go that connects to a [lnd node](https://github.com/lightningnetwork/lnd/blob/master/docs/INSTALL.md) and exposes a simple HTTP JSON API to create and monitor invoices. It comes with a configurable personal payment website and offers a JavaScript widget to integrate in existing websites. -If [webln](https://github.com/wbobeirne/webln) is available the widget automatically use webln to request the payment; +If [webln](https://github.com/wbobeirne/webln) is available the widget automatically use webln to request the payment; otherwise an overlay will be shown with the payment request and a QR code. ## Motivation @@ -19,7 +19,7 @@ BTCPay Server is too big and hard to run for that and I do not need most of its ## Installation -LnMe connects to your [LND node](https://github.com/lightningnetwork/lnd/blob/master/docs/INSTALL.md), so a running LND node is required. +LnMe connects to your [LND node](https://github.com/lightningnetwork/lnd/blob/master/docs/INSTALL.md), so a running LND node is required. LnMe can easily run next to LND on the same system. 1. Download the latest [release](https://github.com/bumi/lnme/releases) @@ -55,6 +55,7 @@ Instead of the path to the macaroon and cert files you can also provide the hex * `static-path`: Path to a folder that you want to serve with LnMe (e.g. /home/bitcoin/lnme/website). Use this if you want to customize your ⚡website. default: disabled * `disable-website`: Disable the default LnMe website. Disable the website if you only want to embed the LnMe widget on your existing website. * `disable-cors`: Disable CORS headers. (default: false) +* `disable-ln-address`: Disable [Lightning Address](https://lightningaddress.com/) handling. * `port`: Port to listen on. (default: 1323) * `request-limit`: Limit the allowed requests per second. (default: 5) @@ -89,7 +90,7 @@ All environment variables must be prefixed by `LNME_` use `_` instead of `-` It is the easiest to run LnMe on the same node as LND. But you can run it anywhere as long as your LND node is accessible. -#### Heroku +#### Heroku One click deployment with Heroku: [![Deploy on Heroku](https://www.herokucdn.com/deploy/button.svg)](https://heroku.com/deploy?template=https://github.com/bumi/lnme) @@ -109,6 +110,16 @@ lnme.michaelbumann.com { `$ caddy --config /etc/caddy/Caddyfile` +### Lightning Address + +The Lightning Address is an Internet Identifier that allows anyone to send you Bitcoin over the Lightning Network. +Lightning Address builds on [LNURL-pay](https://github.com/fiatjaf/lnurl-rfc/blob/luds/06.md) LnMe handles the necessary requests for you. + +For more information check out the website: [lightningaddress.com](https://lightningaddress.com/) + +Your Lightning Address: `{anything}@{your domain}` + + ### Customize your ⚡ website LnMe comes with a default website but you can easily configure and build your own using the the LnMe JavaScript widget or JSON API. diff --git a/lnme.go b/lnme.go index c0d0264..74b5539 100644 --- a/lnme.go +++ b/lnme.go @@ -1,14 +1,19 @@ package main import ( + "crypto/sha256" + "encoding/hex" "flag" + "fmt" "log" "net/http" "os" + "strconv" "strings" rice "github.com/GeertJohan/go.rice" "github.com/bumi/lnme/ln" + "github.com/bumi/lnme/lnurl" "github.com/didip/tollbooth/v6" "github.com/didip/tollbooth/v6/limiter" "github.com/knadh/koanf" @@ -137,6 +142,45 @@ func main() { return c.JSON(http.StatusOK, invoice) }) + if !cfg.Bool("disable-ln-address") { + e.GET("/.well-known/lnurlp/:name", func(c echo.Context) error { + name := c.Param("name") + lightningAddress := name + "@" + c.Request().Host + lnurlMetadata := "[\"text/identifier\", \"" + lightningAddress + "\"], [\"text/plain\", \"Sats for " + lightningAddress + "\"]" + + if amount := c.QueryParam("amount"); amount == "" { + lnurlPayResponse1 := lnurl.LNURLPayResponse1{ + LNURLResponse: lnurl.LNURLResponse{Status: "OK"}, + Callback: fmt.Sprintf("%s://%s%s", c.Scheme(), c.Request().Host, c.Request().URL.Path), + MinSendable: 1000, + MaxSendable: 100000000, + EncodedMetadata: lnurlMetadata, + CommentAllowed: 0, + Tag: "payRequest", + } + return c.JSON(http.StatusOK, lnurlPayResponse1) + } else { + stdOutLogger.Printf("New LightningAddress request amount: %s", amount) + msats, err := strconv.ParseInt(amount, 10, 64) + if err != nil || msats < 1000 { + return c.JSON(http.StatusOK, lnurl.LNURLErrorResponse{Status: "ERROR", Reason: "Invalid Amount"}) + } + sats := msats / 1000 // we need sats + metadataHash := sha256.Sum256([]byte(lnurlMetadata)) + memo := hex.EncodeToString(metadataHash[:]) + invoice, err := lnClient.AddInvoice(sats, memo) + lnurlPayResponse2 := lnurl.LNURLPayResponse2{ + LNURLResponse: lnurl.LNURLResponse{Status: "OK"}, + PR: invoice.PaymentRequest, + Routes: make([][]lnurl.RouteInfo, 0), + Disposable: false, + SuccessAction: &lnurl.SuccessAction{Tag: "message", Message: "Thanks, payment received!"}, + } + return c.JSON(http.StatusOK, lnurlPayResponse2) + } + }) + } + // Debug test endpoint e.GET("/ping", func(c echo.Context) error { return c.JSON(http.StatusOK, "pong") @@ -157,6 +201,7 @@ func LoadConfig() *koanf.Koanf { f.String("lnd-macaroon-path", "~/.lnd/data/chain/bitcoin/mainnet/invoice.macaroon", "Path to the LND macaroon file.") f.String("lnd-cert-path", "~/.lnd/tls.cert", "Path to the LND tls.cert file.") f.Bool("disable-website", false, "Disable default embedded website.") + f.Bool("disable-ln-address", false, "Disable Lightning Address handling") f.Bool("disable-cors", false, "Disable CORS headers.") f.Float64("request-limit", 5, "Request limit per second.") f.String("static-path", "", "Path to a static assets directory.") diff --git a/lnurl/types.go b/lnurl/types.go new file mode 100644 index 0000000..ba60c09 --- /dev/null +++ b/lnurl/types.go @@ -0,0 +1,52 @@ +// THANKS: https://github.com/fiatjaf/go-lnurl/blob/d50a8e916232580895822178fe36e0f5cf400554/base.go +// only using the LNURL types here +package lnurl + +import "net/url" + +type LNURLResponse struct { + Status string `json:"status,omitempty"` + Reason string `json:"reason,omitempty"` +} + +type LNURLPayResponse1 struct { + LNURLResponse + Callback string `json:"callback"` + CallbackURL *url.URL `json:"-"` + Tag string `json:"tag"` + MaxSendable int64 `json:"maxSendable"` + MinSendable int64 `json:"minSendable"` + EncodedMetadata string `json:"metadata"` + Metadata Metadata `json:"-"` + CommentAllowed int64 `json:"commentAllowed"` +} + +type LNURLPayResponse2 struct { + LNURLResponse + SuccessAction *SuccessAction `json:"successAction"` + Routes [][]RouteInfo `json:"routes"` + PR string `json:"pr"` + Disposable bool `json:"disposable,omitempty"` +} + +type RouteInfo struct { + NodeId string `json:"nodeId"` + ChannelUpdate string `json:"channelUpdate"` +} + +type SuccessAction struct { + Tag string `json:"tag"` + Description string `json:"description,omitempty"` + URL string `json:"url,omitempty"` + Message string `json:"message,omitempty"` + Ciphertext string `json:"ciphertext,omitempty"` + IV string `json:"iv,omitempty"` +} + +type LNURLErrorResponse struct { + Status string `json:"status,omitempty"` + Reason string `json:"reason,omitempty"` + URL *url.URL `json:"-"` +} + +type Metadata [][]string From 1515765c3fae7e40a7425ffdf2c195f98a8ef810 Mon Sep 17 00:00:00 2001 From: Michael Bumann Date: Sat, 28 Aug 2021 22:28:03 +0200 Subject: [PATCH 5/7] Fix LNURL metadata --- lnme.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lnme.go b/lnme.go index 74b5539..4be8cf0 100644 --- a/lnme.go +++ b/lnme.go @@ -146,7 +146,7 @@ func main() { e.GET("/.well-known/lnurlp/:name", func(c echo.Context) error { name := c.Param("name") lightningAddress := name + "@" + c.Request().Host - lnurlMetadata := "[\"text/identifier\", \"" + lightningAddress + "\"], [\"text/plain\", \"Sats for " + lightningAddress + "\"]" + lnurlMetadata := "[[\"text/identifier\", \"" + lightningAddress + "\"], [\"text/plain\", \"Sats for " + lightningAddress + "\"]]" if amount := c.QueryParam("amount"); amount == "" { lnurlPayResponse1 := lnurl.LNURLPayResponse1{ From 20e2d8b7e9444c314b42cf775469e9d0fc1f5192 Mon Sep 17 00:00:00 2001 From: Michael Bumann Date: Sat, 28 Aug 2021 22:46:50 +0200 Subject: [PATCH 6/7] readme --- README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 28c31ea..3ee3ff7 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,13 @@ # LnMe - your friendly ⚡ payment page -LnMe is a personal Bitcoin Lightning payment website and payment widget. +LnMe is a personal Bitcoin Lightning payment page/widget and self-hosted [Lightning Address](https://lightningaddress.com/) server. + ![demo](./lnme-demo.gif) -**See it in action: [ln.michaelbumann.com](https://ln.michaelbumann.com/)** +**See it in action: [ln.michaelbumann.com](https://ln.michaelbumann.com/) - my lightning address: bumi@ln.michaelbumann.com** -LnMe focusses on simplicity and ease of deployment. It connects to an existing lightning node and provides a configurable personal payment page and offers a JavaScript widget to integrate into existing websites. +LnMe focusses on simplicity and ease of deployment. It connects to an existing lightning node (currently LND is supported). LnMe is one [simple executable](https://github.com/bumi/lnme/releases) file that can be deployed anywhere with no dependencies. (on your own node or for example with [one click on Heroku](#heroku)) From 031daed750cc60ebf2f44f41b5db8b5eb309b66c Mon Sep 17 00:00:00 2001 From: Michael Bumann Date: Sun, 29 Aug 2021 09:00:23 +0200 Subject: [PATCH 7/7] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3ee3ff7..38a509a 100644 --- a/README.md +++ b/README.md @@ -89,7 +89,7 @@ LnMe needs the following LND permissions: Use the LND [macaroon bakery](http://macaroon-bakery.freedomnode.com/) to create a new macaroon for LnMe. -To get the HEX versions of the files use `xxd -ps -u -c 100000` e.g. `cat invoice.macaroon | xxd -ps -u -c 100000` +To get the HEX versions of the files use `xxd -plain` e.g. `xxd -plain invoice.macaroon | tr -d '\n'` ### Deployment