feat: add lnurl with lightning.pub

This commit is contained in:
Amarpreet Minhas 2024-08-31 14:10:32 -04:00
parent 6b6d6a9610
commit ef6b900cbf
6 changed files with 132 additions and 5 deletions

1
go.mod
View file

@ -4,6 +4,7 @@ go 1.23.0
require ( require (
git.devvul.com/asara/gologger v0.8.0 git.devvul.com/asara/gologger v0.8.0
github.com/davecgh/go-spew v1.1.1
github.com/fiatjaf/eventstore v0.8.1 github.com/fiatjaf/eventstore v0.8.1
github.com/fiatjaf/khatru v0.8.0 github.com/fiatjaf/khatru v0.8.0
github.com/jmoiron/sqlx v1.4.0 github.com/jmoiron/sqlx v1.4.0

View file

@ -0,0 +1,87 @@
package lightningpub
import (
"encoding/json"
"fmt"
"io/ioutil"
"net"
"net/http"
"git.devvul.com/asara/gologger"
"git.devvul.com/asara/well-goknown/config"
"github.com/jmoiron/sqlx"
)
var (
DB *sqlx.DB
)
type lnurlp struct {
Tag string `json:"tag"`
Callback string `json:"callback"`
MaxSendable int64 `json:"maxSendable"`
MinSendable int64 `json:"minSendable"`
Metadata string `json:"metadata"`
AllowsNostr bool `json:"allowsNostr"`
NostrPubkey string `json:"nostrPubkey"`
}
func GetLnurlp(w http.ResponseWriter, r *http.Request) {
l := gologger.Get(config.GetConfig().LogLevel).With().Str("context", "lightningpub").Logger()
// normalize domain
domain, _, err := net.SplitHostPort(r.Host)
if err != nil {
domain = r.Host
}
name := r.PathValue("name")
var lnwallet string
err = DB.QueryRow("SELECT wallet FROM lnwallets WHERE name=$1 AND domain=$2", name, domain).Scan(&lnwallet)
if err != nil {
l.Debug().Msgf("user (%s@%s) doesn't exist: %s", name, domain, err.Error())
http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
return
}
//upstreamUrl := fmt.Sprintf("https://%s/api/guest/lnurl_pay/info?k1=%s", domain, lnwallet)
upstreamUrl := fmt.Sprintf("https://%s/api/guest/lnurl_pay/info?k1=%s", domain, lnwallet)
upstreamPayload, err := http.Get(upstreamUrl)
if err != nil {
l.Debug().Msgf("user (%s@%s) doesn't exist: %s", name, domain, err.Error())
http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
return
}
defer upstreamPayload.Body.Close()
body, err := ioutil.ReadAll(upstreamPayload.Body)
if err != nil {
l.Debug().Msgf("user (%s@%s) doesn't exist: %s", name, domain, err.Error())
http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
return
}
lnurlpReturn := lnurlp{}
err = json.Unmarshal(body, &lnurlpReturn)
if err != nil {
l.Debug().Msgf("user (%s@%s) doesn't exist: %s", name, domain, err.Error())
http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
return
}
m := fmt.Sprintf("[[\"text/plain\", \"ln address payment to a user on the devvul server\"],[\"text/identifier\", \"%s@%s\"]]", name, domain)
lnurlpReturn.Metadata = m
ret, err := json.Marshal(lnurlpReturn)
if err != nil {
l.Debug().Msgf("user (%s@%s) doesn't exist: %s", name, domain, err.Error())
http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
return
}
l.Debug().Msgf("returning lnwallet for %s@%s", name, domain)
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
w.Write(ret)
return
}

View file

@ -6,6 +6,7 @@ import (
"git.devvul.com/asara/gologger" "git.devvul.com/asara/gologger"
"git.devvul.com/asara/well-goknown/config" "git.devvul.com/asara/well-goknown/config"
"git.devvul.com/asara/well-goknown/db" "git.devvul.com/asara/well-goknown/db"
"git.devvul.com/asara/well-goknown/lightningpub"
"git.devvul.com/asara/well-goknown/matrix" "git.devvul.com/asara/well-goknown/matrix"
"git.devvul.com/asara/well-goknown/nostr" "git.devvul.com/asara/well-goknown/nostr"
"github.com/fiatjaf/eventstore/postgresql" "github.com/fiatjaf/eventstore/postgresql"
@ -20,12 +21,12 @@ func main() {
db, _ := db.NewDB() db, _ := db.NewDB()
defer db.Close() defer db.Close()
lightningpub.DB = db
nostr.DB = db nostr.DB = db
nostr.RelayDb = postgresql.PostgresBackend{DatabaseURL: config.GetConfig().DbUrl} nostr.RelayDb = postgresql.PostgresBackend{DatabaseURL: config.GetConfig().DbUrl}
if err := nostr.RelayDb.Init(); err != nil { if err := nostr.RelayDb.Init(); err != nil {
l.Panic().Msgf("unable to connect to relay db: %s", err.Error()) l.Panic().Msgf("unable to connect to relay db: %s", err.Error())
} }
relay := nostr.NewRelay(Version) relay := nostr.NewRelay(Version)
// matrix endpoints // matrix endpoints
@ -38,6 +39,10 @@ func main() {
http.HandleFunc("/.well-known/nostr.json", nostr.GetNostrAddr) http.HandleFunc("/.well-known/nostr.json", nostr.GetNostrAddr)
http.Handle("/relay", relay) http.Handle("/relay", relay)
// lnurlp endpoint
l.Debug().Msg("enabling lnurlp well-known endpoint")
http.HandleFunc("/.well-known/lnurlp/{name}", lightningpub.GetLnurlp)
// start server // start server
port := config.GetConfig().ListenAddr port := config.GetConfig().ListenAddr
l.Info().Msgf("starting server on %s", port) l.Info().Msgf("starting server on %s", port)

View file

@ -0,0 +1,5 @@
BEGIN;
DROP TRIGGER IF EXISTS update_lnwallets_update_ts on lnwallets;
DROP FUNCTION IF EXISTS lnwallets_update_ts();
DROP TABLE IF EXISTS lnwallets;
COMMIT;

View file

@ -0,0 +1,29 @@
BEGIN;
CREATE TABLE IF NOT EXISTS lnwallets (
id SERIAL PRIMARY KEY,
owner_id INT NOT NULL,
name TEXT UNIQUE NOT NULL,
domain TEXT NOT NULL,
wallet TEXT NOT NULL,
create_ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
update_ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
CONSTRAINT fk_owner FOREIGN KEY(owner_id) REFERENCES users(id),
CONSTRAINT ck_name CHECK (name ~ '^[a-z0-9]*$')
);
CREATE FUNCTION lnwallets_update_ts()
RETURNS TRIGGER AS $$
BEGIN
NEW.update_ts = now();
RETURN NEW;
END;
$$ language 'plpgsql';
CREATE TRIGGER update_lnwallets_update_ts
BEFORE UPDATE
ON
lnwallets
FOR EACH ROW
EXECUTE PROCEDURE lnwallets_update_ts();
COMMIT;

View file

@ -14,8 +14,8 @@ func RejectUnregisteredNpubs(ctx context.Context, event *nostr.Event) (reject bo
var err error var err error
l := gologger.Get(config.GetConfig().LogLevel).With().Str("context", "nostr-reject-unregistered").Logger() l := gologger.Get(config.GetConfig().LogLevel).With().Str("context", "nostr-reject-unregistered").Logger()
// always allow seals, lightning ephemeral messages, auth messages // always allow seals, lightning ephemeral messages, auth messages, addressable events
if event.Kind == 13 || event.Kind == 21000 || event.Kind == 22242 || event.Kind == 30078 { if event.Kind == 13 || event.Kind == 21000 || event.Kind == 22242 || event.Kind == 30078 || event.Kind == 1059 {
return false, "" return false, ""
} }
@ -27,8 +27,8 @@ func RejectUnregisteredNpubs(ctx context.Context, event *nostr.Event) (reject bo
} }
npubs := []string{authenticatedUser} npubs := []string{authenticatedUser}
// add recipients of dms (nip04)/private dms (nip17)/gift wraps (nip59) to npubs list // add recipients of dms/private dms/gift wraps/signature requests to npubs list
if event.Kind == 4 || event.Kind == 14 || event.Kind == 1059 { if event.Kind == 4 || event.Kind == 14 || event.Kind == 1059 || event.Kind == 24133 {
for _, npub := range event.Tags.GetAll([]string{"p"}) { for _, npub := range event.Tags.GetAll([]string{"p"}) {
npubs = append(npubs, npub.Value()) npubs = append(npubs, npub.Value())
} }