From f5169c3a3491f565f913ef4a44ece63dcfc8dc52 Mon Sep 17 00:00:00 2001 From: callebtc <93376500+callebtc@users.noreply.github.com> Date: Fri, 31 Oct 2025 17:49:28 +0100 Subject: [PATCH 1/3] add e tag, improve wording, clarify proof tags in nostr event --- 61.md | 45 ++++++++++++++++++++++++++++++++++----------- 1 file changed, 34 insertions(+), 11 deletions(-) diff --git a/61.md b/61.md index 5ef7f680b8..ef63fd47a1 100644 --- a/61.md +++ b/61.md @@ -6,14 +6,14 @@ Nutzaps `draft` `optional` -A Nutzap is a P2PK Cashu token in which the payment itself is the receipt. +A Nutzap is an event containing a [P2PK-locked](https://github.com/cashubtc/nuts/blob/main/11.md) Cashu token that is publicly verifiable. ## High-level flow -Alice wants to nutzap 1 sat to Bob because of an event `event-id-1` she liked. +Alice wants to nutzap 1 sat to Bob for an event `nutzapped-event-id` she liked. ### Alice nutzaps Bob 1. Alice fetches event `kind:10019` from Bob to see the mints Bob trusts. -2. She mints a token at that mint (or swaps some tokens she already had in that mint) P2PK-locked to the pubkey Bob has listed in his `kind:10019`. +2. She mints or swaps ecash at that mint with a P2PK lock that contains the pubkey Bob listed in his `kind:10019` and an `e` tag of the event `nutzapped-event-id` to zap. 3. She publishes a `kind:9321` event to the relays Bob indicated with the proofs she minted. ### Bob receives the nutzap @@ -40,9 +40,29 @@ Alice wants to nutzap 1 sat to Bob because of an event `event-id-1` she liked. * `pubkey`: Public key that MUST be used to P2PK-lock receiving nutzaps -- implementations MUST NOT use the target user's main Nostr public key. This public key corresponds to the `privkey` field encrypted in a user's [nip-60](60.md) _wallet event_. ### Nutzap event -Event `kind:9321` is a nutzap event published by the sender, p-tagging the recipient. The outputs are P2PK-locked to the public key the recipient indicated in their `kind:10019` event. +Event `kind:9321` is a nutzap event published by the sender, p-tagging the recipient. The outputs are P2PK-locked to the public key the recipient indicated in their `kind:10019` event and with a P2PK secret that commits to the event id `nutzapped-event-id` to zap. -Clients MUST prefix the public key they P2PK-lock with `"02"` (for nostr<>cashu compatibility). +##### Cashu proof +A NIP-61 zap contains one or more Cashu proofs with a [NUT-12](https://github.com/cashubtc/nuts/blob/main/11.md) P2PK lock. The proof's `secret` has the receiver's `p2pk-pubkey` in `data` and the zapped event's id `e` tag. The proof also contains a [NUT-12](https://github.com/cashubtc/nuts/blob/main/12.md) DLEQ field `dleq`. + +Clients MUST prefix the receiver's `p2pk-pubkey` with `"02"` (for nostr<>cashu compatibility). +```jsonc +{ + "amount":2, + "C":"0302f4aacbfa5da455b884501ebcbd4c9c3cc6640daa7269f640aa70522727b790", + "id":"00da2cc2b0016589", + "secret":"[\"P2PK\", {\"data\": \"029128d0918cd2241e92968c2d5f8eb6ba9b6adf3cd7bd0a5c82c64400470f9a86\", \"nonce\": \"ec2180143de3341e47eeaa1ec41d37fc\", \"tags\": [[\"e\", \"nutzapped-event-id\"]]}]", + "dleq":{ + "r":"e5f0c29513ef7e0b0b1798be8f7ca7940296833a7e723e16ba2162299c77d183", + "s":"12fdde66b4bc26fc76f11f88aae527fb1642c0fb4066d06db8a3c6976a224205", + "e":"9df01b6421926fd04521185d3264eed99a4c0951d8c247f0f8f1244d340d5e06" + } +} +``` + +##### Nostr event + +The nutzap event contains one or more serialized Cashu proofs. ```jsonc { @@ -50,9 +70,9 @@ Clients MUST prefix the public key they P2PK-lock with `"02"` (for nostr<>cashu "content": "Thanks for this great idea.", "pubkey": "", "tags": [ - [ "proof", "{\"amount\":1,\"C\":\"02277c66191736eb72fce9d975d08e3191f8f96afb73ab1eec37e4465683066d3f\",\"id\":\"000a93d6f8a1d2c4\",\"secret\":\"[\\\"P2PK\\\",{\\\"nonce\\\":\\\"b00bdd0467b0090a25bdf2d2f0d45ac4e355c482c1418350f273a04fedaaee83\\\",\\\"data\\\":\\\"02eaee8939e3565e48cc62967e2fde9d8e2a4b3ec0081f29eceff5c64ef10ac1ed\\\"}]\"}" ], - [ "u", "https://stablenut.umint.cash" ], - [ "e", "", "" ], + [ "proof", "{\"amount\": 2, \"C\": \"0302f4aacbfa5da455b884501ebcbd4c9c3cc6640daa7269f640aa70522727b790\", \"id\": \"00da2cc2b0016589\", \"secret\": \"[\\\"P2PK\\\", {\\\"data\\\": \\\"029128d0918cd2241e92968c2d5f8eb6ba9b6adf3cd7bd0a5c82c64400470f9a86\\\", \\\"nonce\\\": \\\"ec2180143de3341e47eeaa1ec41d37fc\\\", \\\"tags\\\": [[\\\"e\\\", \\\"nutzapped-event-id\\\"]]}]\", \"dleq\": {\"r\": \"e5f0c29513ef7e0b0b1798be8f7ca7940296833a7e723e16ba2162299c77d183\", \"s\": \"12fdde66b4bc26fc76f11f88aae527fb1642c0fb4066d06db8a3c6976a224205\", \"e\": \"9df01b6421926fd04521185d3264eed99a4c0951d8c247f0f8f1244d340d5e06\"}}"], + [ "u", "https://stablenut.umint.cash"], + [ "e", "", ""], [ "k", ""], [ "p", "e9fbced3a42dcf551486650cc752ab354347dd413b307484e4fd1818ab53f991" ], // recipient of nutzap ] @@ -61,11 +81,13 @@ Clients MUST prefix the public key they P2PK-lock with `"02"` (for nostr<>cashu * `.content` is an optional comment for the nutzap * `.tags`: - * `proof` is one or more proofs P2PK-locked to the public key the recipient specified in their `kind:10019` event and including a DLEQ proof. + * `proof` is one P2PK-locked proof to the public key the recipient specified in their `kind:10019` event, an optional `e` tag committing to `nutzapped-event-id`, and a DLEQ proof. Multiple `proof` tags may be added - one proof per tag. * `u` is the mint the URL of the mint EXACTLY as specified by the recipient's `kind:10019`. * `p` is the Nostr identity public key of nutzap recipient. * `e` is the event that is being nutzapped, if any. +Note that the optional `e` tag is contained in the proof's `secret` as well as in the Nostr event. + ## Sending a nutzap * The sender fetches the recipient's `kind:10019`. @@ -110,9 +132,10 @@ Events that redeem a nutzap SHOULD be published to the sender's [NIP-65](65.md) When listing or counting zaps received by any given event, observer clients SHOULD: * check that the receiving user has issued a `kind:10019` tagging the mint where the cashu has been minted. -* check that the token is locked to the pubkey the user has listed in their `kind:10019`. +* check that the Cashu proofs are locked to the pubkey the user has listed in their `kind:10019`. +* check that the Cashu proofs' secrets contain an `e` tag that equals the zapped event's id. * look at the `u` tag and check that the token is issued in one of the mints listed in the `kind:10019`. -* locally verify the DLEQ proof of the tokens being sent. +* verify the DLEQs of the proofs using the public keysets of the mint. All these checks can be done offline (as long as the observer has the receiver mints' keyset and their `kind:10019` event), so the process should be reasonably fast. From d1cd02b562e0e9a5862199b748ac7b752f4d1878 Mon Sep 17 00:00:00 2001 From: callebtc <93376500+callebtc@users.noreply.github.com> Date: Fri, 31 Oct 2025 17:59:28 +0100 Subject: [PATCH 2/3] fix proof --- 61.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/61.md b/61.md index 83a087bd53..2c088defed 100644 --- a/61.md +++ b/61.md @@ -70,7 +70,7 @@ The nutzap event contains one or more serialized Cashu proofs. "content": "Thanks for this great idea.", "pubkey": "", "tags": [ - [ "proof", "{\"amount\":1,\"C\":\"02277c66191736eb72fce9d975d08e3191f8f96afb73ab1eec37e4465683066d3f\",\"id\":\"000a93d6f8a1d2c4\",\"secret\":\"[\\\"P2PK\\\",{\\\"nonce\\\":\\\"b00bdd0467b0090a25bdf2d2f0d45ac4e355c482c1418350f273a04fedaaee83\\\",\\\"data\\\":\\\"02eaee8939e3565e48cc62967e2fde9d8e2a4b3ec0081f29eceff5c64ef10ac1ed\\\"}]\"}" ], + [ "proof", "{\"amount\": 2, \"C\": \"0302f4aacbfa5da455b884501ebcbd4c9c3cc6640daa7269f640aa70522727b790\", \"id\": \"00da2cc2b0016589\", \"secret\": \"[\\\"P2PK\\\", {\\\"data\\\": \\\"029128d0918cd2241e92968c2d5f8eb6ba9b6adf3cd7bd0a5c82c64400470f9a86\\\", \\\"nonce\\\": \\\"ec2180143de3341e47eeaa1ec41d37fc\\\", \\\"tags\\\": [[\\\"e\\\", \\\"nutzapped-event-id\\\"]]}]\", \"dleq\": {\"r\": \"e5f0c29513ef7e0b0b1798be8f7ca7940296833a7e723e16ba2162299c77d183\", \"s\": \"12fdde66b4bc26fc76f11f88aae527fb1642c0fb4066d06db8a3c6976a224205\", \"e\": \"9df01b6421926fd04521185d3264eed99a4c0951d8c247f0f8f1244d340d5e06\"}}"], [ "unit", "sat" ], [ "u", "https://stablenut.umint.cash" ], [ "e", "", "" ], From 7811c1d5cacf902cad5f03ee9c08d40ddb546d5c Mon Sep 17 00:00:00 2001 From: callebtc <93376500+callebtc@users.noreply.github.com> Date: Fri, 31 Oct 2025 18:21:18 +0100 Subject: [PATCH 3/3] add P tag to Cashu proofs secret --- 61.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/61.md b/61.md index 2c088defed..148aaf79ae 100644 --- a/61.md +++ b/61.md @@ -13,7 +13,7 @@ Alice wants to nutzap 1 sat to Bob for an event `nutzapped-event-id` she liked. ### Alice nutzaps Bob 1. Alice fetches event `kind:10019` from Bob to see the mints Bob trusts. -2. She mints or swaps ecash at that mint with a P2PK lock that contains the pubkey Bob listed in his `kind:10019` and an `e` tag of the event `nutzapped-event-id` to zap. +2. She mints or swaps ecash at that mint with a P2PK lock that contains the pubkey Bob listed in his `kind:10019`, a `P` tag with the sender's pubkey, and an `e` tag of the event `nutzapped-event-id` to zap. 3. She publishes a `kind:9321` event to the relays Bob indicated with the proofs she minted. ### Bob receives the nutzap @@ -43,7 +43,7 @@ Alice wants to nutzap 1 sat to Bob for an event `nutzapped-event-id` she liked. Event `kind:9321` is a nutzap event published by the sender, p-tagging the recipient. The outputs are P2PK-locked to the public key the recipient indicated in their `kind:10019` event and with a P2PK secret that commits to the event id `nutzapped-event-id` to zap. ##### Cashu proof -A NIP-61 zap contains one or more Cashu proofs with a [NUT-12](https://github.com/cashubtc/nuts/blob/main/11.md) P2PK lock. The proof's `secret` has the receiver's `p2pk-pubkey` in `data` and the zapped event's id `e` tag. The proof also contains a [NUT-12](https://github.com/cashubtc/nuts/blob/main/12.md) DLEQ field `dleq`. +A NIP-61 zap contains one or more Cashu proofs with a [NUT-12](https://github.com/cashubtc/nuts/blob/main/11.md) P2PK lock. The proof's `secret` has the receiver's `p2pk-pubkey` in `data` field. The `tags` field of the `secret` contains a `P` tag with the sender's pubkey `sender-pubkey` and the zapped event's id `e`. The proof also contains a [NUT-12](https://github.com/cashubtc/nuts/blob/main/12.md) DLEQ field `dleq`. Clients MUST prefix the receiver's `p2pk-pubkey` with `"02"` (for nostr<>cashu compatibility). ```jsonc @@ -51,7 +51,7 @@ Clients MUST prefix the receiver's `p2pk-pubkey` with `"02"` (for nostr<>cashu c "amount":2, "C":"0302f4aacbfa5da455b884501ebcbd4c9c3cc6640daa7269f640aa70522727b790", "id":"00da2cc2b0016589", - "secret":"[\"P2PK\", {\"data\": \"029128d0918cd2241e92968c2d5f8eb6ba9b6adf3cd7bd0a5c82c64400470f9a86\", \"nonce\": \"ec2180143de3341e47eeaa1ec41d37fc\", \"tags\": [[\"e\", \"nutzapped-event-id\"]]}]", + "secret":"[\"P2PK\", {\"data\": \"029128d0918cd2241e92968c2d5f8eb6ba9b6adf3cd7bd0a5c82c64400470f9a86\", \"nonce\": \"43e0d7c14e20616850f540b5dec96006\", \"tags\": [[\"e\", \"nutzapped-event-id\"], [\"P\", \"sender-pubkey\"]]}]", "dleq":{ "r":"e5f0c29513ef7e0b0b1798be8f7ca7940296833a7e723e16ba2162299c77d183", "s":"12fdde66b4bc26fc76f11f88aae527fb1642c0fb4066d06db8a3c6976a224205", @@ -70,7 +70,7 @@ The nutzap event contains one or more serialized Cashu proofs. "content": "Thanks for this great idea.", "pubkey": "", "tags": [ - [ "proof", "{\"amount\": 2, \"C\": \"0302f4aacbfa5da455b884501ebcbd4c9c3cc6640daa7269f640aa70522727b790\", \"id\": \"00da2cc2b0016589\", \"secret\": \"[\\\"P2PK\\\", {\\\"data\\\": \\\"029128d0918cd2241e92968c2d5f8eb6ba9b6adf3cd7bd0a5c82c64400470f9a86\\\", \\\"nonce\\\": \\\"ec2180143de3341e47eeaa1ec41d37fc\\\", \\\"tags\\\": [[\\\"e\\\", \\\"nutzapped-event-id\\\"]]}]\", \"dleq\": {\"r\": \"e5f0c29513ef7e0b0b1798be8f7ca7940296833a7e723e16ba2162299c77d183\", \"s\": \"12fdde66b4bc26fc76f11f88aae527fb1642c0fb4066d06db8a3c6976a224205\", \"e\": \"9df01b6421926fd04521185d3264eed99a4c0951d8c247f0f8f1244d340d5e06\"}}"], + [ "proof", "{\"amount\": 2, \"C\": \"0302f4aacbfa5da455b884501ebcbd4c9c3cc6640daa7269f640aa70522727b790\", \"id\": \"00da2cc2b0016589\", \"secret\": \"[\\\"P2PK\\\", {\\\"data\\\": \\\"029128d0918cd2241e92968c2d5f8eb6ba9b6adf3cd7bd0a5c82c64400470f9a86\\\", \\\"nonce\\\": \\\"43e0d7c14e20616850f540b5dec96006\\\", \\\"tags\\\": [[\\\"e\\\", \\\"nutzapped-event-id\\\"], [\\\"P\\\", \\\"sender-pubkey\\\"]]}]\", \"dleq\": {\"r\": \"e5f0c29513ef7e0b0b1798be8f7ca7940296833a7e723e16ba2162299c77d183\", \"s\": \"12fdde66b4bc26fc76f11f88aae527fb1642c0fb4066d06db8a3c6976a224205\", \"e\": \"9df01b6421926fd04521185d3264eed99a4c0951d8c247f0f8f1244d340d5e06\"}}"], [ "unit", "sat" ], [ "u", "https://stablenut.umint.cash" ], [ "e", "", "" ], @@ -82,13 +82,13 @@ The nutzap event contains one or more serialized Cashu proofs. * `.content` is an optional comment for the nutzap * `.tags`: - * `proof` is one P2PK-locked proof to the public key the recipient specified in their `kind:10019` event, an optional `e` tag committing to `nutzapped-event-id`, and a DLEQ proof. Multiple `proof` tags may be added - one proof per tag. + * `proof` is one P2PK-locked proof to the public key the recipient specified in their `kind:10019` event, a `P` tag committing to the sender's pubkey `sender-pubkey`, an optional `e` tag committing to `nutzapped-event-id`, and a DLEQ proof. Multiple `proof` tags may be added - one proof per tag. * `unit` the base unit the proofs are denominated in (eg: `sat`, `usd`, `eur`). Default: `sat` if omitted. * `u` is the mint the URL of the mint EXACTLY as specified by the recipient's `kind:10019`. * `p` is the Nostr identity public key of nutzap recipient. * `e` is the event that is being nutzapped, if any. -Note that the optional `e` tag is contained in the proof's `secret` as well as in the Nostr event. +Note that the optional `e` tag is contained in the Cashu proof's `secret` as well as in the Nostr event. The `P` tag in the Cashu proof is the `pubkey` in the nutzap event. ## Sending a nutzap @@ -136,6 +136,7 @@ When listing or counting zaps received by any given event, observer clients SHOU * check that the receiving user has issued a `kind:10019` tagging the mint where the cashu has been minted. * check that the Cashu proofs are locked to the pubkey the user has listed in their `kind:10019`. +* check that the Cashu proof's secret contains a `P` that equals the `pubkey` of the nutzap event. * check that the Cashu proofs' secrets contain an `e` tag that equals the zapped event's id. * look at the `u` tag and check that the token is issued in one of the mints listed in the `kind:10019`. * verify the DLEQs of the proofs using the public keysets of the mint.