From 681282a401df367087e4c347209ed5a461070b8e Mon Sep 17 00:00:00 2001 From: William Casarin Date: Sat, 28 Jan 2023 07:42:51 -0800 Subject: [PATCH 1/8] Lightning Zaps --- 57.md | 143 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ README.md | 1 + 2 files changed, 144 insertions(+) create mode 100644 57.md diff --git a/57.md b/57.md new file mode 100644 index 0000000000..1ffed5f5f2 --- /dev/null +++ b/57.md @@ -0,0 +1,143 @@ +NIP-57 +====== + +Lightning Zaps +-------------- + +`draft` `optional` `author:jb55` `author:kieran` + +This NIP defines a new note type called a lightning zap of kind `9735`. These represent paid lightning invoice receipts sent by a lightning node called the `zapper`. We also define a another note type of kind `9734` which are `zap request` notes, which will be described in this document. + +Having lightning receipts on nostr allows clients to display lightning payments from entities on the network. These can be used for fun or for spam deterrence. + + +## Definitions + +`zapper` - the lightning node or service that sends zap notes (kind `9735`) + +`zap request` - a note of kind `9734` created by the person zapping + +`zap invoice` - the bolt11 invoice fetched from a custom lnurl endpoint which contains a `zap request` note + + +## Protocol flow + +### Client side + +1. Calculate the lnurl pay request url for a user from the lud06 or lud16 field on their profile + +2. Fetch the lnurl pay request static endpoint (`https://host.com/.well-known/lnurlp/user`) and gather the `allowsNostr` and `nostrPubkey` fields. If `allowsNostr` exists and it is `true`, and if `nostrPubkey` exists and is true, associate this information with the user. The `nostrPubkey` is the `zapper`'s pubkey, and it is used to authorize zaps sent to that user. + +3. Clients may choose to display a lightning zap button on each post or on the users profile, if the user's lnurl pay request endpoint supports nostr, the client SHOULD generate a `zap invoice` instead of a normal lnurl invoice. + +4. To generate a `zap invoice`, call the `callback` url with `amount` set to the milli-satoshi amount value. A `nostr` querystring value MUST be set as well. It a uri-encoded `zap request` note signed by the user's key. The `zap request` note contains an `e` tag of the note it is zapping, and a `p` tag of the target user's pubkey. The `e` tag is optional which allows profile tipping. The `zap request` note must also have a `relays` tag, which is gathered from the user's configured relays. + +5. Pay this invoice or pass it to an app that can pay the invoice. Once it's paid, a `zap note` will be created by the `zapper`. + +### LNURL Server side + +The lnurl server will need some additional pieces of information so that clients can know that zap invoices are supported: + +1. Add a `nostrPubkey` to the lnurl-pay static endpoint `/.well-known/lnurlp/user`, where `nostrPubkey` is the nostr pubkey of the `zapper`, the entity that creates zap notes. Clients will use this to authorize zaps. + +2. Add an `allowsNostr` field and set it to true. + +3. In the lnurl-pay callback URL, watch for a `nostr` querystring, where the contents of the note is a uri-encoded `zap request` JSON. + +4. If present, the zap request note must be validated: + + a. It MUST have a valid nostr signature + + b. It MUST have tags + + c. It MUST have at least one p-tag + + d. It MUST have either 0 or 1 e-tag + + e. There should be a `relays` tag with the relays to send the `zap` note to. + +5. If valid, fetch an invoice where the description is this note and this note only. No additional lnurl metadata is included in the description. + +At this point, the lightning node is ready to send the zap note once payment is received. + +## The zap note + +Zap notes are created by a lightning node reacting to paid invoices. Zap notes are only created when the invoice description (committed to the description hash) contains a `zap request` note. + +Example zap note: + +```json +{ + "id": "67b48a14fb66c60c8f9070bdeb37afdfcc3d08ad01989460448e4081eddda446", + "pubkey": "9630f464cca6a5147aa8a35f0bcdd3ce485324e732fd39e09233b1d848238f31", + "created_at": 1674164545, + "kind": 9735, + "tags": [ + [ + "p", + "32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245" + ], + [ + "e", + "3624762a1274dd9636e0c552b53086d70bc88c165bc4dc0f9e836a1eaf86c3b8" + ], + [ + "bolt11", + "lnbc10u1p3unwfusp5t9r3yymhpfqculx78u027lxspgxcr2n2987mx2j55nnfs95nxnzqpp5jmrh92pfld78spqs78v9euf2385t83uvpwk9ldrlvf6ch7tpascqhp5zvkrmemgth3tufcvflmzjzfvjt023nazlhljz2n9hattj4f8jq8qxqyjw5qcqpjrzjqtc4fc44feggv7065fqe5m4ytjarg3repr5j9el35xhmtfexc42yczarjuqqfzqqqqqqqqlgqqqqqqgq9q9qxpqysgq079nkq507a5tw7xgttmj4u990j7wfggtrasah5gd4ywfr2pjcn29383tphp4t48gquelz9z78p4cq7ml3nrrphw5w6eckhjwmhezhnqpy6gyf0" + ], + [ + "description", + "{\"pubkey\":\"32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245\",\"content\":\"\",\"id\":\"d9cc14d50fcb8c27539aacf776882942c1a11ea4472f8cdec1dea82fab66279d\",\"created_at\":1674164539,\"sig\":\"77127f636577e9029276be060332ea565deaf89ff215a494ccff16ae3f757065e2bc59b2e8c113dd407917a010b3abd36c8d7ad84c0e3ab7dab3a0b0caa9835d\",\"kind\":9734,\"tags\":[[\"e\",\"3624762a1274dd9636e0c552b53086d70bc88c165bc4dc0f9e836a1eaf86c3b8\"],[\"p\",\"32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245\"],[\"relays\",\"wss://relay.damus.io\",\"wss://nostr-relay.wlvs.space\",\"wss://nostr.fmt.wiz.biz\",\"wss://relay.nostr.bg\",\"wss://nostr.oxtr.dev\",\"wss://nostr.v0l.io\",\"wss://brb.io\",\"wss://nostr.bitcoiner.social\",\"ws://monad.jb55.com:8080\",\"wss://relay.snort.social\"]]}" + ], + [ + "preimage", + "5d006d2cf1e73c7148e7519a4c68adc81642ce0e25a432b2434c99f97344c15f" + ] + ], + "content": "", + "sig": "b0a3c5c984ceb777ac455b2f659505df51585d5fd97a0ec1fdb5f3347d392080d4b420240434a3afd909207195dac1e2f7e3df26ba862a45afd8bfe101c2b1cc" + } +``` + +* The zap note MUST have a `bolt11` tag containing the description hash bolt11 invoice. + +* The zap note MUST contain a `description` tag which is the invoice description. + +* `SHA256(description)` MUST match the description hash in the bolt11 invoice. + +* The zap note MAY contain a `preimage` to match against the payment hash of the bolt11 invoice. This isn't really a payment proof, there is no real way to prove that the invoice is real or has been paid. You are trusting the author of the zap note for the legitimacy of the payment. + +The zap note is not a proof of payment, all it proves is that some nostr user fetched an invoice. The existence of the zap note implies the invoice as paid, but it could be a lie given a rogue implementation. + + +### Creating a zap note + +When receiving a payment, the following steps are executed: + +1. Get the description for the invoice. This needs to be saved somewhere during the generation of the description hash invoice. It is saved automatically for you with CLN, which is the reference implementation used here. + +2. Parse the bolt11 description as a JSON nostr note. You SHOULD check the signature of the parsed note to ensure that it is valid. This is the `zap request` note created by the entity who is zapping. + +4. The note MUST have only one `p` tag + +5. The note MUST have 0 or 1 `e` tag + +6. Create a nostr note of kind `9735` that includes the `p` tag AND optional `e` tag. The content SHOULD be empty. The created_at date SHOULD be set to the invoice paid_at date for idempotency. + +A reference implementation for the zapper is here: [zapper][zapper] + +[zapper]: https://github.com/jb55/cln-nostr-zapper + + +## Client Behavior + +Clients MAY fetch zap notes on posts and profiles: + +`{"kinds": [9735], "#p": [...]}` + +To authorize these notes, clients MUST fetch the `nostrPubkey` from the users configured lightning address or lnurl and ensure that the zaps to their posts were created by this pubkey. If clients don't do this, anyone could forge unauthorized zaps. + + +## Future Work + +Zaps can be extended to be more private by encrypting zap request notes to the target user, but for simplicity it has been left out of this initial draft. diff --git a/README.md b/README.md index 829e61ba7c..093fb448e2 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,7 @@ NIPs stand for **Nostr Implementation Possibilities**. They exist to document wh - [NIP-40: Expiration Timestamp](40.md) - [NIP-42: Authentication of clients to relays](42.md) - [NIP-50: Keywords filter](50.md) +- [NIP-57: Lightning Zaps](57.md) ## Event Kinds From 3b8f89b7444c886998d74478c64b50500b24a0b6 Mon Sep 17 00:00:00 2001 From: William Casarin Date: Sun, 5 Feb 2023 12:02:02 -0800 Subject: [PATCH 2/8] zaps: more client behavior notes --- 57.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/57.md b/57.md index 1ffed5f5f2..8af3eb553c 100644 --- a/57.md +++ b/57.md @@ -133,10 +133,11 @@ A reference implementation for the zapper is here: [zapper][zapper] Clients MAY fetch zap notes on posts and profiles: -`{"kinds": [9735], "#p": [...]}` +`{"kinds": [9735], "#e": [...]}` To authorize these notes, clients MUST fetch the `nostrPubkey` from the users configured lightning address or lnurl and ensure that the zaps to their posts were created by this pubkey. If clients don't do this, anyone could forge unauthorized zaps. +Once authorized, clients MAY tally zaps on posts, and list them on profiles. If the zap request note contains a non-empty `content`, it may display a zap comment. Generally clients should show users the `zap request` note, and use the `zap note` to show "zap authorized by ..." but this is optional. ## Future Work From 138441a1fc8ab926ef6376c64ae6d8811a33229c Mon Sep 17 00:00:00 2001 From: William Casarin Date: Sun, 5 Feb 2023 12:04:33 -0800 Subject: [PATCH 3/8] Update 57.md Co-authored-by: 0xtr --- 57.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/57.md b/57.md index 8af3eb553c..7df94e3cc0 100644 --- a/57.md +++ b/57.md @@ -6,7 +6,7 @@ Lightning Zaps `draft` `optional` `author:jb55` `author:kieran` -This NIP defines a new note type called a lightning zap of kind `9735`. These represent paid lightning invoice receipts sent by a lightning node called the `zapper`. We also define a another note type of kind `9734` which are `zap request` notes, which will be described in this document. +This NIP defines a new note type called a lightning zap of kind `9735`. These represent paid lightning invoice receipts sent by a lightning node called the `zapper`. We also define another note type of kind `9734` which are `zap request` notes, which will be described in this document. Having lightning receipts on nostr allows clients to display lightning payments from entities on the network. These can be used for fun or for spam deterrence. From 25ec328e3ab32082865b5d37b559c2c20d47039f Mon Sep 17 00:00:00 2001 From: William Casarin Date: Sun, 5 Feb 2023 12:05:15 -0800 Subject: [PATCH 4/8] zaps: typo --- 57.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/57.md b/57.md index 7df94e3cc0..12154f77f0 100644 --- a/57.md +++ b/57.md @@ -30,7 +30,7 @@ Having lightning receipts on nostr allows clients to display lightning payments 3. Clients may choose to display a lightning zap button on each post or on the users profile, if the user's lnurl pay request endpoint supports nostr, the client SHOULD generate a `zap invoice` instead of a normal lnurl invoice. -4. To generate a `zap invoice`, call the `callback` url with `amount` set to the milli-satoshi amount value. A `nostr` querystring value MUST be set as well. It a uri-encoded `zap request` note signed by the user's key. The `zap request` note contains an `e` tag of the note it is zapping, and a `p` tag of the target user's pubkey. The `e` tag is optional which allows profile tipping. The `zap request` note must also have a `relays` tag, which is gathered from the user's configured relays. +4. To generate a `zap invoice`, call the `callback` url with `amount` set to the milli-satoshi amount value. A `nostr` querystring value MUST be set as well. It is a uri-encoded `zap request` note signed by the user's key. The `zap request` note contains an `e` tag of the note it is zapping, and a `p` tag of the target user's pubkey. The `e` tag is optional which allows profile tipping. The `zap request` note must also have a `relays` tag, which is gathered from the user's configured relays. 5. Pay this invoice or pass it to an app that can pay the invoice. Once it's paid, a `zap note` will be created by the `zapper`. From a442c5887018a0ae96b023a96982e9a0eb13effe Mon Sep 17 00:00:00 2001 From: William Casarin Date: Sun, 5 Feb 2023 12:09:54 -0800 Subject: [PATCH 5/8] zaps: make note of comments --- 57.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/57.md b/57.md index 12154f77f0..680b9e4603 100644 --- a/57.md +++ b/57.md @@ -30,7 +30,7 @@ Having lightning receipts on nostr allows clients to display lightning payments 3. Clients may choose to display a lightning zap button on each post or on the users profile, if the user's lnurl pay request endpoint supports nostr, the client SHOULD generate a `zap invoice` instead of a normal lnurl invoice. -4. To generate a `zap invoice`, call the `callback` url with `amount` set to the milli-satoshi amount value. A `nostr` querystring value MUST be set as well. It is a uri-encoded `zap request` note signed by the user's key. The `zap request` note contains an `e` tag of the note it is zapping, and a `p` tag of the target user's pubkey. The `e` tag is optional which allows profile tipping. The `zap request` note must also have a `relays` tag, which is gathered from the user's configured relays. +4. To generate a `zap invoice`, call the `callback` url with `amount` set to the milli-satoshi amount value. A `nostr` querystring value MUST be set as well. It is a uri-encoded `zap request` note signed by the user's key. The `zap request` note contains an `e` tag of the note it is zapping, and a `p` tag of the target user's pubkey. The `e` tag is optional which allows profile tipping. The `zap request` note must also have a `relays` tag, which is gathered from the user's configured relays. The `content` MAY be an additional comment from the user which can be displayed when listing zaps on posts and profiles. 5. Pay this invoice or pass it to an app that can pay the invoice. Once it's paid, a `zap note` will be created by the `zapper`. From bfb63e1f79315419a892c53c8bdd8ccccb63be77 Mon Sep 17 00:00:00 2001 From: William Casarin Date: Sun, 5 Feb 2023 12:13:31 -0800 Subject: [PATCH 6/8] zaps: specify that is should be a description hash invoice --- 57.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/57.md b/57.md index 680b9e4603..5c890f8b27 100644 --- a/57.md +++ b/57.md @@ -56,7 +56,7 @@ The lnurl server will need some additional pieces of information so that clients e. There should be a `relays` tag with the relays to send the `zap` note to. -5. If valid, fetch an invoice where the description is this note and this note only. No additional lnurl metadata is included in the description. +5. If valid, fetch a description hash invoice where the description is this note and this note only. No additional lnurl metadata is included in the description. At this point, the lightning node is ready to send the zap note once payment is received. From 11f5ba55cfca4e3ef9593b19de9ab5ed96a5769c Mon Sep 17 00:00:00 2001 From: William Casarin Date: Sun, 5 Feb 2023 12:20:05 -0800 Subject: [PATCH 7/8] zaps: don't forget to send to relays! --- 57.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/57.md b/57.md index 5c890f8b27..13727845f3 100644 --- a/57.md +++ b/57.md @@ -124,6 +124,8 @@ When receiving a payment, the following steps are executed: 6. Create a nostr note of kind `9735` that includes the `p` tag AND optional `e` tag. The content SHOULD be empty. The created_at date SHOULD be set to the invoice paid_at date for idempotency. +7. Send the note to the `relays` declared in the `zap request` note from the invoice description. + A reference implementation for the zapper is here: [zapper][zapper] [zapper]: https://github.com/jb55/cln-nostr-zapper From 14adbbc1e13d26bc920b58457520225bc2b3a32e Mon Sep 17 00:00:00 2001 From: William Casarin Date: Sun, 5 Feb 2023 12:44:50 -0800 Subject: [PATCH 8/8] zaps: feedback from the ben --- 57.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/57.md b/57.md index 13727845f3..78a3fd639d 100644 --- a/57.md +++ b/57.md @@ -26,7 +26,7 @@ Having lightning receipts on nostr allows clients to display lightning payments 1. Calculate the lnurl pay request url for a user from the lud06 or lud16 field on their profile -2. Fetch the lnurl pay request static endpoint (`https://host.com/.well-known/lnurlp/user`) and gather the `allowsNostr` and `nostrPubkey` fields. If `allowsNostr` exists and it is `true`, and if `nostrPubkey` exists and is true, associate this information with the user. The `nostrPubkey` is the `zapper`'s pubkey, and it is used to authorize zaps sent to that user. +2. Fetch the lnurl pay request static endpoint (`https://host.com/.well-known/lnurlp/user`) and gather the `allowsNostr` and `nostrPubkey` fields. If `allowsNostr` exists and it is `true`, and if `nostrPubkey` exists and is a valid BIP 340 public key, associate this information with the user. The `nostrPubkey` is the `zapper`'s pubkey, and it is used to authorize zaps sent to that user. 3. Clients may choose to display a lightning zap button on each post or on the users profile, if the user's lnurl pay request endpoint supports nostr, the client SHOULD generate a `zap invoice` instead of a normal lnurl invoice.