Private lightning zaps are a way to send a zap while not revealing the zap author. Only the receiver of the zap can see who sent the zap, and may include an optional private message.
A private zap request
is used to send a zap to a user but only revealing the sender's identity to the receiver.
An anon
tag is added to the zap request
event with the encrypted private zap request
event.
The private zap event
is encrypted using NIP-04 between the zap request
key and the recipient's pubkey. The zap request
key is the ephemeral key used to sign the zap request
event.
The encrypted private zap request
event is then added to the zap request
event as the anon
tag bech32 encoded with the hrp
of pzap1
and the iv
values of the encryption are added with an underscore followed by the iv being bech32 encoded wit the hrp
of iv
.
Example Private Zap Event:
{
"id": "9bf0e1d812e10faa5fd3d2560637f8e99fcbfff478583d2c835754d791acfda5",
"pubkey": "385c3a6ec0b9d57a4330dbd6284989be5bd00e41c535f9ca39b6ae7c521b81cd",
"created_at": 1708467377,
"kind": 9733,
"tags": [
[
"p",
"aa4fc8665f5696e33db7e1a572e3b0f5b3d615837b0f362dcb1c8068b098c7b4"
]
],
"content": "Private Zap message!",
"sig": "48cc725483f41207e83c48f3d01a4e5d341bedcf556e158c19b9faac42bca30e92113049268522cd5052ab3c69850c5df7e2b4fb40c72ce770996edee5a57c8d"
}
Example Private Zap Request Event:
{
"id": "4ef9df0a6c7dd8b761145acc7055ae077df716bd8c374496d1f598c5b63bcd7a",
"pubkey": "79d4773fded68a3ea9a30f13e651ff83e150957dacb0c2f6038883d837e1b17b",
"created_at": 1708466564,
"kind": 9734,
"tags": [
[
"p",
"aa4fc8665f5696e33db7e1a572e3b0f5b3d615837b0f362dcb1c8068b098c7b4"
],
[
"relays",
"wss://relay.damus.io"
],
[
"anon",
"pzap1n0pkup9fxc9w3yd2tvfr03shffrapfz8rtzu9kkq6v222jw5rgtp9myyh378gdtwptpnls8f0rv0v2dyapgt7sssu4263puepgshsj9g4u9y5lvfv9fsujlgvywsuejvftlfzcanu5fmnf2a3grlelwe8v0z4mdkyhr9mddxpswtvp7mtlc4acdys7740t0x5ej36qs5amfzwz5dpwlaf4gsl69lzhqdgc3hgt62xw4y8384a6zvsnf96l3ardkd2vkk6cm77p6v7ul3gwgjr7tra7uzpkvf4hncxp5qd75h6cdadf6n2d7edhc3dyyy7qpdka2mgqhvckhzhd2gcaux34jyw6qfk3nxhaaqs6pqkuy6z34wu2p2fvqqvg55eyqlrndjlgekm7xu08lqc3g0nje59uqu0adqerv2puypez3eck9xzupg4vxyfclk37qfqxra8nt4tk9ydc2tzhpnl4wpf7jf2nrkchknfnfgmezfyqe074dexe5mkxgw67j7zn8s24tae8tml747qnq0edw5jxsx6xfc4qhshf3man0s5duw6wm63ue8fese8c7hanqzphjna3g0ee4jgpwceqzk9jgrvf9rnkt89tkvh75qm65nvtqpud30vecwlqzdlu9fhcaj7jv89gpy32y2k828vsj7x8hmlq55rleeq23e062apenymv96tkvltv266ww6kly2q2t7k6z_iv189a0s9afn7ehz4gpeanueh56cv6t79qk"
]
],
"content": "",
"sig": "95984f4403a4f414436270584a2ea1e3b83c988a0794ed8438f7d214714de3d982edf5eaf494362333ee07b2ce49c64c1d1206ec3914e9c7d7d4bf958d8bbfad"
}
Ephemeral keys can be generated deterministically by generating a private key like so:
func generate_private_keypair(our_privkey: Privkey, id: NoteId, created_at: UInt32) -> FullKeypair? {
let to_hash = our_privkey.hex() + id.hex() + String(created_at)
guard let dat = to_hash.data(using: .utf8) else {
return nil
}
let privkey_bytes = sha256(dat)
let privkey = Privkey(privkey_bytes)
guard let pubkey = privkey_to_pubkey(privkey: privkey) else { return nil }
return FullKeypair(pubkey: pubkey, privkey: privkey)
}
The private key is generated using the sha256 of the following data concatenated together:
- our_privkey: is the user sending the notes private key
- id: the target note that you are zapping's note id
- created_at: the target note's created_at as a string
This allows you to decode private zaps that you have sent yourself by attempting to generate a matching pubkey from public data on the target note
Note that this will not work on the web, since NIP-07 does not support hashing the private key into other data.