fix: anonymous profile zaps were encrypted as private zaps; nutzap chips reply on long-press

The user-only LnZapRequestEvent.create overload marked ANONYMOUS requests
with a blank-valued anon tag, which the signer treats as an unsigned
private zap: the message was encrypted to the recipient under the
throwaway key instead of staying public. Use the valueless anon tag, as
the event-targeted overload already does. Adds a regression test.

Also carries the nutzap note into the notification gallery chips so the
long-press reply-to-zap gesture works for NIP-61 nutzaps too — no extra
tagging needed there since nutzaps are signed by the sender.

https://claude.ai/code/session_01LM3KTECMMAdNBHZfs1dANa
This commit is contained in:
Claude
2026-06-10 23:41:26 +00:00
parent e4ce7b887d
commit c6b09c4b53
4 changed files with 30 additions and 1 deletions
@@ -373,6 +373,7 @@ fun RenderNutzapGallery(
user = note.author,
comment = event?.content?.ifBlank { null },
amount = showAmount(java.math.BigDecimal(sats)),
zapNote = note,
)
}.toImmutableList()
}
@@ -84,6 +84,7 @@ fun NutzapUserSetCompose(
user = note.author,
comment = event?.content?.ifBlank { null },
amount = showAmount(java.math.BigDecimal(sats)),
zapNote = note,
)
}.toImmutableList()
}
@@ -176,7 +176,10 @@ class LnZapRequestEvent(
}
LnZapEvent.ZapType.ANONYMOUS -> {
tags += arrayOf(arrayOf("anon", ""))
// Valueless `anon` tag: a blank-valued one (`["anon", ""]`) is the
// marker for an *unsigned private* zap and would make the throwaway
// signer encrypt the message instead of keeping it public.
tags += arrayOf(arrayOf("anon"))
NostrSignerInternal(KeyPair()).sign(createdAt, KIND, tags, message)
}
@@ -88,6 +88,30 @@ class LnZapRequestAnonTagTest {
assertFalse(zapRequest.pubKey == signer.pubKey, "anonymous zaps must be signed by a throwaway key")
}
/**
* Regression test for the user-only (profile zap) overload: its ANONYMOUS
* branch used a blank-valued `anon` tag, which [NostrSignerInternal] treats
* as an unsigned *private* zap and encrypts — silently turning a public
* anonymous comment into an encrypted one nobody but the recipient can read.
*/
@Test
fun `anonymous profile zap request keeps the message public`() =
runTest {
val zapRequest =
LnZapRequestEvent.create(
userHex = receiverPubKey,
relays = relays,
signer = signer,
message = "great work",
zapType = LnZapEvent.ZapType.ANONYMOUS,
)
assertTrue(zapRequest.hasAnonTag())
assertFalse(zapRequest.isPrivateZap(), "anonymous zaps must not be encrypted as private zaps")
assertEquals("great work", zapRequest.content)
assertFalse(zapRequest.pubKey == signer.pubKey, "anonymous zaps must be signed by a throwaway key")
}
@Test
fun `private zap request has anon tag, is private, and hides the sender key`() =
runTest {