{"id":"576020674e748e3130f403bb4a04ae0fd30fc4d83ffda861ef13767b376a9a19","pubkey":"266815e0c9210dfa324c6cba3573b14bee49da4209a9456f9484e5106cd408a5","created_at":1779228799,"kind":30817,"tags":[["d","NIP-SP"],["title","Nostr silent payment address"],["client","nostrhub.io"]],"content":"> WARNING: I have no idea what I'm doing, this is an exploration of BIP-352 and one way it could be used with nostr public keys\n\n# NIP-SP: Nostr-Derived Silent Payments\n\n`draft` `optional`\n\nThis document defines how to send and receive [BIP-352 silent payments](https://github.com/bitcoin/bips/blob/master/bip-0352.mediawiki) using a Nostr public key (`npub`) as the sole identity input. Every Nostr keypair deterministically maps to a silent-payment address (`sp1q…`) without any setup, on-chain footprint, or out-of-band publication: the recipient's BIP-352 scan and spend keys are derived from `npub` via public additive tweaks, so any sender can construct the recipient's silent-payment address from their `npub` alone. Each payment produces a unique Taproot output with full BIP-352 privacy semantics on the receiving side.\n\n## Document structure\n\nThis NIP is organized in two independent parts.\n\n- **Part 1 — Bitcoin-side (§1–§6)** is a self-contained Bitcoin protocol. Given an `npub` it defines the BIP-352 address; given an `nsec` it defines how to receive (chain-scan) and spend (PSBT). No Nostr events are involved. A wallet that implements only Part 1 is fully BIP-352-interoperable.\n- **Part 2 — Nostr integration (§7–§8)** defines optional Nostr event kinds that improve UX on top of Part 1: a per-payment receipt notification (kind 8352) and an encrypted UTXO-cache event (kind 10353). A wallet MAY implement Part 1 without Part 2.\n\nCross-cutting concerns — security, compatibility, test vectors, references — follow as §9–§12.\n\n## ⚠️ Critical Security Warning\n\n**The derivation in this NIP is NOT hardened.** `bscan` and `bspend` are both `nsec + (publicly known tweak) mod n`, so revealing *either* private key is mathematically equivalent to revealing `nsec` itself — anyone holding the disclosed key plus the user's `npub` can recover `nsec` with one subtraction (see §9.1 for the derivation).\n\nConsequences a wallet MUST internalize:\n\n- **`bscan` and `bspend` MUST be treated with exactly the same care as `nsec`.** They are not \"view keys\" in the conventional BIP-352 sense; for npub-derived wallets they are full identity-compromising secrets.\n- **Silent-payment wallets derived from a Nostr identity MUST NOT use any third-party scanning service that requires the user to hand over `bscan`.** Doing so is operationally identical to publishing the user's `nsec`. This includes any remote indexer, hosted wallet, watch-only server, or \"scan-for-me\" relay that takes `bscan` as input.\n- **Only two scanning architectures are safe:** (a) local scanning on the user's own device, and (b) a \"tweak-data\" service that returns *only* public per-transaction tweaks and lets the wallet finish the ECDH locally with `bscan` never leaving the device. See §5.1.\n- Users who genuinely need a remote scanning service that ingests `bscan` MUST use a separate BIP-352 wallet whose keys are independent of any Nostr identity — never an npub-derived wallet.\n\nIf your wallet UI exposes `bscan` to the user (e.g. for export), it MUST display the same warnings as for `nsec` export.\n\n## Motivation\n\nAny static single-address derivation from a Nostr key — for example, treating `npub` directly as a BIP-341 Taproot internal key — gives the user one reusable Bitcoin address. Reusing a single address across multiple payments leaks the cluster of payments to public chain analysis. BIP-352 solves this by producing a unique output per payment, derived non-interactively from the recipient's two-key silent-payment address.\n\nA direct BIP-352 deployment requires recipients to generate and back up a second pair of keys (scan + spend). This NIP derives both keys deterministically from the user's existing Nostr keypair via **public additive tweaks** of `npub`, so:\n\n- There is no second secret to back up — `nsec` remains the sole secret.\n- The recipient does not need to publish anything before they can be paid. Any sender that knows their `npub` can derive their silent-payment address and pay them.\n- The mapping is symmetric: the sender derives `(Bscan, Bspend)` from `npub`; the recipient derives `(bscan, bspend)` from `nsec`. Both arrive at the same key pair.\n\nThe trade-off relative to a hardened (one-way) derivation is that `bscan` and `bspend` are arithmetically linked to `nsec` via publicly known tweaks. Disclosing either compromises the corresponding Nostr identity, so they MUST be treated with the same care as `nsec` itself. In particular, this NIP does not permit handing `bscan` to a remote scanning service (see §5.1 and §9.1).\n\n---\n\n# Part 1 — Bitcoin-Side: Silent Payment Address Derivation and Spending\n\nThis part defines everything required to interoperate with BIP-352 against a Nostr-derived silent-payment address. It uses `npub` and `nsec` purely as cryptographic inputs; no Nostr relays, events, or network calls are involved.\n\n## 1. Definitions\n\n| Symbol | Meaning |\n|---|---|\n| `n` | Order of the secp256k1 curve |\n| `G` | secp256k1 generator point |\n| `nsec` | The recipient's raw Nostr private key (32-byte scalar) |\n| `npub` | The recipient's Nostr public key (32-byte x-only, per BIP-340) |\n| `P_npub` | The point `lift_x(npub)` — even-y by BIP-340 convention |\n| `d_npub` | The BIP-340-normalized scalar such that `d_npub · G = P_npub` |\n| `t_scan`, `t_spend` | Public per-identity additive tweaks (§2.1) |\n| `bscan`, `bspend` | Derived BIP-352 scan/spend private keys |\n| `Bscan`, `Bspend` | Derived BIP-352 scan/spend public keys |\n| `tₖ` | BIP-352 per-output tweak for output index `k` |\n| `m` | BIP-352 label scalar (`0` ⇒ unlabeled / change) |\n| `Pₖ` | The on-chain Taproot output key for output `k` |\n| `H_tagged(tag, msg)` | BIP-340 tagged hash: `SHA256(SHA256(tag) ‖ SHA256(tag) ‖ msg)` |\n\nAll scalars are mod `n` unless stated otherwise. Public keys are serialized in compressed SEC1 form (33 bytes) except where x-only is required.\n\n`d_npub` is the BIP-340-normalized form of `nsec`:\n\n```\nd_npub = nsec        if (nsec · G) has even y\n       = n − nsec    otherwise\n```\n\nThis is the same normalization Nostr already applies when signing per BIP-340; no new state needs to be stored.\n\n## 2. Key Derivation\n\n### 2.1 Additive derivation from `npub`\n\nBoth the per-identity tweaks and the resulting scan/spend keys are derived as follows.\n\n**Tweaks (public; either side can compute):**\n\n```\nt_scan  = H_tagged(\"nostr-sp/scan\",  npub) mod n\nt_spend = H_tagged(\"nostr-sp/spend\", npub) mod n\n```\n\n**Sender side** (uses only `npub`):\n\n```\nBscan   = P_npub + t_scan  · G\nBspend  = P_npub + t_spend · G\n```\n\n**Recipient side** (uses `nsec`):\n\n```\nbscan   = (d_npub + t_scan)  mod n\nbspend  = (d_npub + t_spend) mod n\n```\n\nBy construction, `bscan · G = Bscan` and `bspend · G = Bspend`, and both sides arrive at the same `(Bscan, Bspend)` pair without any communication.\n\nThe tagged-hash input is the 32-byte x-only `npub`. The tagged hash provides strong domain separation between the two tweaks and against any other protocol that uses additive tweaks of `npub`.\n\nBecause `npub` is publicly known, both tweaks are publicly computable. The privacy of `bscan` and `bspend` rests entirely on the privacy of `nsec`: knowledge of `bscan` and `npub` trivially recovers `d_npub = (bscan − t_scan) mod n`, which is the BIP-340 signing key of the Nostr identity. The same property holds for `bspend`.\n\n> ⚠️ **`bscan` and `bspend` are NOT \"view keys\" — they are full identity-compromising secrets.** Either one, combined with the user's (publicly known) `npub`, lets an attacker recover `nsec`. They MUST NOT be uploaded to any third-party scanning service, hosted wallet, or remote indexer. See the **Critical Security Warning** at the top of this document and §9.1 for the full security model.\n\n### 2.2 Edge cases\n\n- If `t_scan = 0`, `t_spend = 0`, `bscan = 0`, `bspend = 0`, or any derived scalar is ≥ `n`, the implementation MUST reject the derivation and surface an error. The probability of this is cryptographically negligible.\n- The change label `m = 0` is always implicitly registered, per BIP-352. Recipients MUST scan for the change label.\n\n## 3. Address Encoding\n\nThe silent-payment address is a standard BIP-352 address (Bech32m, HRP `\"sp\"` for mainnet, `\"tsp\"` for testnet/signet), with payload `serP(Bscan) ‖ serP(Bspend)` where `serP` is the 33-byte compressed SEC1 encoding. The address always starts with `sp1q…` (version 0).\n\nWallets MAY surface the encoded `sp1q…` string in their UI, but the npub-derived form is canonical: the address can always be recomputed from `npub` alone, and implementations SHOULD treat it as a derived view of the identity rather than a separately stored field.\n\n### 3.1 Network scope\n\nImplementations MUST NOT mix HRPs across networks within a single wallet session. The same `nsec` can be used to derive addresses for any network supported by BIP-352; the choice of HRP selects the network.\n\n## 4. Sending\n\nA sender follows the standard BIP-352 sending procedure. Given a recipient `npub`:\n\n1. Derive `(Bscan, Bspend)` from `npub` per §2.1. No profile lookup, relay query, or declaration event is required.\n2. Choose inputs from the sender's wallet. All inputs MUST be of types permitted by BIP-352 (P2TR, P2WPKH, P2SH-P2WPKH, P2PKH). Outputs from SegWit version > 1 MUST NOT be spent in the same transaction.\n3. Compute the shared secret per BIP-352:\n   ```\n   a            = a₁ + a₂ + … + aₙ   (sum of input private keys, mod n)\n   A            = a · G              (sum of input public keys)\n   input_hash   = H_tagged(\"BIP0352/Inputs\", outpointL ‖ A)\n   shared       = input_hash · a · Bscan\n   ```\n4. For each output to this recipient at index `k` (per BIP-352 output ordering rules):\n   ```\n   tₖ = H_tagged(\"BIP0352/SharedSecret\", serP(shared) ‖ ser32(k))\n   Pₖ = Bspend + tₖ · G            (no label)\n       = Bspend + (tₖ + m) · G     (with label m)\n   ```\n5. Place a P2TR output `OP_1 <xonly(Pₖ)>` in the transaction.\n6. Sign with `SIGHASH_DEFAULT` or `SIGHASH_ALL`. `SIGHASH_ANYONECANPAY` MUST NOT be used: the shared secret commits to the set of inputs and cannot be re-derived after input substitution.\n\n**Recipient capability.** Every `npub` maps to a silent-payment address via §2.1, but the recipient can only spend incoming SP UTXOs if they have a wallet that implements this NIP. This NIP defines no capability signal; how a sender confirms recipient capability — and what they do when it is absent — is out of scope. Paying an SP address whose owner has no SP-aware wallet results in funds that are recoverable only once the recipient adopts one.\n\n## 5. Receiving (BIP-352 Chain Scan)\n\nThe wallet computes `bscan` and `bspend` per §2.1 and performs the standard BIP-352 scan over the chain (or a relevant subset such as the last N blocks since the last scan checkpoint). For each candidate transaction:\n\n1. Identify eligible inputs (per BIP-352) and compute `A = ΣAᵢ` and `input_hash`.\n2. Compute `shared = input_hash · bscan · A`.\n3. For each Taproot output in the transaction, iterate `k = 0, 1, …` until no match:\n   ```\n   tₖ = H_tagged(\"BIP0352/SharedSecret\", serP(shared) ‖ ser32(k))\n   P_check = Bspend + tₖ · G\n   If xonly(P_check) matches the Taproot output key:\n     → unlabeled UTXO found at this output, store (txid, vout, value, tₖ, m=0)\n   Else:\n     candidate_label = xonly(output) − P_check\n     If candidate_label matches a precomputed label (incl. the change label):\n       → labeled UTXO found, store (txid, vout, value, tₖ, m)\n   ```\n\nThis is the canonical receive path. A wallet that implements only this section is BIP-352-correct: it will discover every UTXO addressed to its npub-derived SP address. Part 2's notification mechanism (§7) is purely an optimization layered over this.\n\n### 5.1 Scanning architectures\n\n> ⚠️ **Because §2.1 is additive (not hardened), `bscan` carries the full sensitivity of `nsec`.** Anyone given `bscan` and the user's `npub` can compute `nsec = (bscan − t_scan)` (up to BIP-340 sign) and sign Nostr events as the user. This is true *by construction* and cannot be mitigated by trust assumptions about the service. Any architecture that ships `bscan` off the user's device is therefore equivalent to publishing `nsec` and is prohibited by this NIP.\n\nTwo — and only two — scanning architectures are permitted:\n\n- **Local scan (REQUIRED to support):** the wallet ingests block data and runs the algorithm locally. No key material leaves the device. This is the default and the safest mode.\n- **Tweak-data service (RECOMMENDED for clients with constrained bandwidth):** an external service exposes only public per-transaction tweak data (e.g. `input_hash · A`) for BIP-352-eligible transactions. The wallet completes the ECDH locally using its private `bscan`. **The service never receives `bscan` or any other private key material.** Wallets choosing this mode MUST verify that the service protocol does not, at any point, ask for `bscan`.\n\nA **full scanning service** — where the wallet uploads `bscan` to a third party that performs the scan and returns matched UTXOs — is **PROHIBITED** under this NIP, because disclosing `bscan` is mathematically equivalent to disclosing `nsec` (§9.1). Wallets MUST NOT offer such a flow against npub-derived keys, MUST NOT accept user configuration that points at such a service, and MUST reject any prompt (UI, NIP-46 bunker request, etc.) that would result in `bscan` being transmitted off-device.\n\nUsers who genuinely need a remote scanning service that ingests the scan key SHOULD use a separate BIP-352 wallet whose `(bscan, bspend)` are generated independently of any Nostr identity. NIP-SP is deliberately not the right tool for that workflow.\n\n## 6. Spending\n\n### 6.1 Output construction and signing chain\n\nEach silent-payment UTXO is a P2TR output whose on-chain key `Pₖ` was constructed directly per BIP-352 — there is no BIP-341 internal key and no script tree. The full derivation chain from `nsec` to the spending scalar is:\n\n```\n─── stage 1: BIP-340 parity-normalize the Nostr key ──────────────────\n  d_npub = nsec        if (nsec · G) has even y\n         = n − nsec    otherwise\n\n─── stage 2: additive spend-key derivation (§2.1) ────────────────────\n  t_spend = H_tagged(\"nostr-sp/spend\", npub) mod n\n  bspend  = (d_npub + t_spend) mod n\n\n─── stage 3: BIP-352 per-output tweak ────────────────────────────────\n  dₖ = (bspend + tₖ + m) mod n\n\n─── stage 4: BIP-340 parity-normalize the output key ─────────────────\n  Pₖ      = dₖ · G\n  d_sign  = dₖ            if Pₖ has even y\n          = n − dₖ        otherwise\n\n─── stage 5: sign ────────────────────────────────────────────────────\n  BIP-340 Schnorr signature with d_sign over the BIP-341 sighash.\n  NO further BIP-341 TapTweak is applied.\n```\n\nExactly two parity corrections appear, both at well-defined points: stage 1 against `nsec` (the standard Nostr/BIP-340 normalization), and stage 4 against the output key (the standard Taproot key-path normalization). All additions in stages 2 and 3 operate on raw scalars mod `n` without further normalization.\n\n**Conformance MUSTs:**\n\n- Implementations MUST NOT apply BIP-341 TapTweak when spending a NIP-SP output. The output key `Pₖ` is *not* an internal key.\n- Implementations MUST apply the stage-1 and stage-4 parity normalizations.\n- Implementations MUST verify `xonly(dₖ · G)` matches the on-chain output key before signing, as a guard against corrupted tweak data.\n\n### 6.2 PSBT vocabulary\n\nThis NIP carries silent-payment metadata in BIP-174 proprietary fields (`PSBT_IN_PROPRIETARY`, key type `0xFC`) under the identifier prefix `\"nostr_sp\"` (8 ASCII bytes). This is BIP-174-conformant and forward-compatible: any signer that does not recognize the prefix will ignore the fields per BIP-371's extensibility clause.\n\nProprietary key encoding:\n\n```\nkey   = 0xFC ‖ compact_size(8) ‖ \"nostr_sp\" ‖ <subtype> ‖ <subkey_data>\nvalue = <subtype-specific bytes>\n```\n\nDefined subtypes:\n\n| Subtype | Subkey | Value | Meaning |\n|---|---|---|---|\n| `0x00` | empty | 2 bytes: `<version 0x00>` ‖ `<scheme 0x01>` | Marker. `scheme = 0x01` ⇒ NIP-SP additive derivation (this NIP). |\n| `0x01` | empty | 32 bytes | Per-output BIP-352 tweak `tₖ`. |\n| `0x02` | empty | 32 bytes | Label scalar `m`. Absent ⇒ unlabeled (`m = 0`). |\n| `0x03` | empty | 32 bytes | Expected on-chain output key `xonly(Pₖ)`. |\n\n**Conformance MUSTs and MUST NOTs at the input level:**\n\n- An input carrying subtype `0x00` MUST also carry subtypes `0x01` and `0x03`. Subtype `0x02` is conditional.\n- An input carrying subtype `0x00` MUST NOT carry `PSBT_IN_TAP_INTERNAL_KEY` (`0x17`).\n- An input carrying subtype `0x00` MUST NOT carry `PSBT_IN_TAP_MERKLE_ROOT` (`0x18`).\n- An input carrying subtype `0x00` MUST carry `PSBT_IN_WITNESS_UTXO` (`0x01`).\n- If `PSBT_IN_SIGHASH_TYPE` (`0x03`) is present, its value MUST be `SIGHASH_DEFAULT` (`0x00`) or `SIGHASH_ALL` (`0x01`).\n- After signing, the signer writes a 64- or 65-byte BIP-340 Schnorr signature into `PSBT_IN_TAP_KEY_SIG` (`0x13`), unchanged from BIP-371. The finalizer behavior for `PSBT_IN_TAP_KEY_SIG` is unchanged.\n\n### 6.3 Signer behavior\n\nA signer (whether local, NIP-07 extension, or NIP-46 bunker) that recognizes the `\"nostr_sp\"` prefix processes each marked input as follows:\n\n```\nfor each input I in psbt:\n    if I has nostr_sp[0x00] with scheme = 0x01:\n        require I has WITNESS_UTXO\n        require I has nostr_sp[0x01]\n        require I has nostr_sp[0x03]\n        require I does NOT have TAP_INTERNAL_KEY\n        require I does NOT have TAP_MERKLE_ROOT\n        require sighash ∈ {DEFAULT, ALL} or absent\n\n        d_npub  = nsec if (nsec · G) has even y else n − nsec\n        t_spend = H_tagged(\"nostr-sp/spend\", npub) mod n\n        bspend  = (d_npub + t_spend) mod n\n        tₖ      = nostr_sp[0x01]\n        m       = nostr_sp[0x02] if present else 0\n        dₖ      = (bspend + tₖ + m) mod n\n\n        // Guard: derived key must match expected output key\n        if xonly(dₖ · G) ≠ nostr_sp[0x03]:\n            abort signing with explicit error\n\n        d_sign = dₖ if (dₖ · G) has even y else n − dₖ\n        sig    = bip340_schnorr_sign(d_sign, taproot_sighash(psbt, I))\n        write PSBT_IN_TAP_KEY_SIG = sig\n```\n\nSigners that do not recognize the prefix MUST NOT attempt to sign such inputs — they will leave the input unsigned, and finalization will fail in a recoverable way (the user can route the PSBT to a signer that does understand NIP-SP).\n\n### 6.4 Migration to a future BIP-352 PSBT standard\n\nA standardized PSBT vocabulary for BIP-352 is under discussion in the Bitcoin developer community but is not finalized at the time of writing. Once that standard exists, this NIP's proprietary fields MAY be replaced by the standard equivalents in a future revision. Signers MAY accept either vocabulary during a transition window. Wallets producing PSBTs SHOULD emit only one form at a time, configurable per signer capability.\n\n---\n\n# Part 2 — Nostr Integration: Notifications and UTXO Cache\n\nThis part defines two OPTIONAL Nostr event kinds that layer additional UX features on top of Part 1. Neither is required for correctness; a wallet that implements only Part 1 will still discover and spend all incoming silent-payment UTXOs via chain scanning.\n\n- **Kind 8352 (§7)** — a sender-to-recipient receipt notification. Lets the recipient credit a specific UTXO without waiting for a chain scan.\n- **Kind 10353 (§8)** — an encrypted self-published cache of the recipient's own UTXO state. Enables cross-device sync and faster wallet recovery.\n\nBoth kinds use the recipient's Nostr identity (the same `npub` that derives the SP address in Part 1) as the receiving / signing key.\n\n## 7. Kind 8352 — Silent Payment Receipt Notification\n\nRegular event used exclusively as the **rumor** inside a [NIP-59](https://github.com/nostr-protocol/nips/blob/master/59.md) gift wrap. It MUST NOT be published directly to relays in the clear.\n\nThe kind number `8352` is `8000 + 352`, encoding the BIP-352 base used throughout this NIP in the regular event range.\n\nThis rumor is a private, sender-to-recipient notification of the per-output tweak needed for the recipient to credit a silent-payment UTXO without performing a full chain scan.\n\n```json\n{\n  \"kind\": 8352,\n  \"pubkey\": \"<sender-pubkey>\",\n  \"content\": \"\",\n  \"tags\": [\n    [\"i\",      \"bitcoin:tx:<txid>\"],\n    [\"vout\",   \"<output-index>\"],\n    [\"amount\", \"<sats>\"],\n    [\"tweak\",  \"<32-byte hex>\"],\n    [\"label\",  \"<32-byte hex>\"],\n    [\"p\",      \"<recipient-pubkey>\"],\n    [\"alt\",    \"Silent payment receipt: 50000 sats\"]\n  ]\n}\n```\n\n### 7.1 Tags\n\n| Tag | Required | Description |\n|---|---|---|\n| `i` | Yes | NIP-73 identifier `bitcoin:tx:<txid>`, with `<txid>` lowercase hex. |\n| `vout` | Yes | Output index in the referenced transaction (decimal integer, ≥ 0). |\n| `amount` | Yes | Output value in satoshis (decimal integer). |\n| `tweak` | Yes | Per-output BIP-352 tweak `tₖ` as 64-char lowercase hex. |\n| `label` | No | Label scalar `m` as 64-char lowercase hex. Omit for unlabeled outputs. |\n| `p` | Yes | Recipient pubkey. MUST match the gift-wrap recipient. |\n| `alt` | Yes | NIP-31 human-readable fallback. |\n\nThe `content` field SHOULD be an empty string. A future revision MAY permit a freeform message; for now, implementations SHOULD ignore it.\n\n### 7.2 Sender behavior\n\nAfter broadcasting a transaction that contains one or more silent-payment outputs (per §4), the sender MAY publish one kind 8352 rumor per output, each gift-wrapped per NIP-59 to the corresponding recipient pubkey. The `tweak` and `label` values are exactly the `tₖ` and `m` the sender computed when constructing the output.\n\nEmitting notifications is OPTIONAL. The on-chain transaction is the source of truth; a missing notification cannot prevent the recipient from discovering the UTXO via §5.\n\n### 7.3 Recipient validation\n\nA wallet that supports this kind MAY use a validated notification to credit a UTXO before its next chain scan completes. For each incoming gift wrap:\n\n1. Decrypt the gift wrap per NIP-59.\n2. Validate the rumor structure (§7.1).\n3. Fetch the referenced transaction from a Bitcoin data source.\n4. Independently verify that output `vout` of the transaction pays `Pₖ` where:\n   ```\n   Pₖ = Bspend + (tweak + label) · G\n   ```\n   (`label` defaults to 0 if absent), and that `xonly(Pₖ)` equals the Taproot output key of `tx.vout[vout]`.\n5. Verify that the output's `value` matches the `amount` tag (within tolerance of one satoshi, in case of integer conversion drift; SHOULD be exact).\n6. If all checks pass, credit the UTXO to the wallet's spendable set with `(txid, vout, value, tweak, label)`.\n\nA failed verification MUST NOT cause the wallet to credit a phantom UTXO. The notification is purely an optimization layered over the on-chain truth; wallets that support it SHOULD still run §5 periodically to backfill payments from senders who did not (or could not) emit notifications.\n\n### 7.4 Spam considerations\n\nBecause gift wraps are unsolicited, an attacker can send arbitrary 8352 events to any pubkey. Wallets MUST treat the on-chain verification (§7.3 step 4) as the gate, not the existence of the notification. Implementations SHOULD rate-limit how aggressively they fetch Bitcoin data in response to unsolicited notifications to avoid being used as a DoS amplifier. Logging discarded notifications MAY help detect grief attacks.\n\n## 8. Kind 10353 — Encrypted Silent Payment UTXO Set (OPTIONAL)\n\nReplaceable event for the recipient's own wallet state. One canonical event per identity. The visible `tags` array carries only public metadata; all UTXO state is carried in a NIP-44-encrypted *tags array* placed in `content`, following the pattern used by NIP-51 encrypted lists.\n\nThe kind number `10353` reflects: replaceable range (10000–19999) + an increment over the BIP-352 base used elsewhere in this NIP.\n\n```json\n{\n  \"kind\": 10353,\n  \"pubkey\": \"<recipient-pubkey>\",\n  \"content\": \"<NIP-44 ciphertext of a JSON-encoded tags array>\",\n  \"tags\": [\n    [\"alt\", \"Encrypted silent payment UTXO set\"]\n  ]\n}\n```\n\n### 8.1 Encryption\n\nThe `content` field is `nip44_encrypt(plaintext, ownPubkey)` where `plaintext = JSON.stringify(<inner tags array>)`. Decryption is the inverse. Self-encryption uses the recipient's own keypair on both sides.\n\n### 8.2 Inner (encrypted) tag schema\n\nAfter decryption, the plaintext is a JSON array of tags following the standard Nostr tag shape (each tag is an array of strings).\n\n| Tag | Cardinality | Description |\n|---|---|---|\n| `scan_height` | Exactly one | Block height through which scanning has been completed, as a decimal string. |\n| `utxo` | Zero or more | One per spendable UTXO. See [UTXO Tag](#utxo-tag) below. |\n\nThe set contains **only spendable UTXOs**. Implementations MUST NOT include spent UTXOs and MUST NOT carry a status flag. When a UTXO is spent, the wallet republishes the event with that `utxo` tag removed; absence from the set is the spent signal.\n\n#### UTXO Tag\n\nFormat: `[\"utxo\", \"<txid>\", \"<vout>\", \"<value>\", \"<tweak>\"]` for unlabeled UTXOs, or `[\"utxo\", \"<txid>\", \"<vout>\", \"<value>\", \"<tweak>\", \"<label>\"]` for labeled UTXOs.\n\n| Index | Description |\n|---|---|\n| 0 | Tag name: `\"utxo\"` |\n| 1 | Transaction ID, 64-char lowercase hex |\n| 2 | Output index, decimal string (≥ 0) |\n| 3 | Output value in satoshis, decimal string |\n| 4 | Per-output BIP-352 tweak `tₖ`, 64-char lowercase hex |\n| 5 | Label scalar `m`, 64-char lowercase hex. Omit for unlabeled outputs. |\n\nExample decrypted plaintext:\n\n```json\n[\n  [\"scan_height\", \"845231\"],\n  [\"utxo\", \"abc123…\", \"2\", \"50000\", \"deadbeef…\", \"001f00…\"],\n  [\"utxo\", \"def456…\", \"0\", \"12000\", \"cafebabe…\"]\n]\n```\n\n### 8.3 Recovery and persistence\n\nThe event is purely a wallet-state cache. Loss of this event MUST NOT cause loss of funds — the wallet can always be reconstructed by performing a fresh chain scan per §5. Wallets SHOULD also keep the UTXO set in local persistent storage and use this event primarily for cross-device sync or recovery.\n\n---\n\n## 9. Security Considerations\n\n### 9.1 Scan- or spend-key disclosure equals `nsec` disclosure\n\n§2.1 derives `bscan` and `bspend` via *public* additive tweaks of `nsec`. The tweaks `t_scan = H_tagged(\"nostr-sp/scan\", npub)` and `t_spend = H_tagged(\"nostr-sp/spend\", npub)` are computable by anyone who knows `npub` (which is, by definition, public).\n\nThe full recovery, given a leaked `bscan` and the target `npub`:\n\n```\nt_scan  = H_tagged(\"nostr-sp/scan\", npub) mod n\nd_npub  = (bscan − t_scan) mod n             ← BIP-340 signing key\nnsec    = d_npub  or  n − d_npub             ← original raw nsec (one of two parities)\n```\n\n`d_npub` is already sufficient to sign arbitrary Nostr events as the user (BIP-340 only ever uses the even-y normalization). The same procedure recovers `d_npub` from a leaked `bspend` using `t_spend` instead. Both keys are therefore identity-compromising secrets, equal in sensitivity to `nsec` itself. There is no cryptographic mitigation — the property is intrinsic to additive derivation and is the explicit trade-off this NIP makes in exchange for letting senders derive addresses from `npub` alone.\n\nOperationally, this means:\n\n- `bscan` and `bspend` MUST be treated with exactly the same care as `nsec`. They MUST NOT be transmitted to, stored on, or processed by any third party.\n- Remote \"full scanning services\" — where the user uploads `bscan` and receives matched UTXOs — are **PROHIBITED** by this NIP (§5.1). Implementations MUST NOT offer such a flow, MUST NOT accept user configuration that enables it, and MUST refuse NIP-07/NIP-46 requests that would export `bscan` or `bspend` to a remote endpoint.\n- The \"tweak-data\" scanning architecture (the service exposes only public per-transaction inputs; the wallet does the ECDH locally) IS supported and is the recommended way to reduce client-side scanning load.\n- Backup and key-export UI MUST display the same warnings for `bscan` and `bspend` as it does for `nsec`.\n\nUsers who want a remote scanner SHOULD instead use a separate BIP-352 wallet whose `(bscan, bspend)` are not derived from a Nostr identity. The trade-off this NIP makes — sender-side ergonomics over scan-key delegation — is deliberate; users with the opposite preference should not use NIP-SP.\n\n### 9.2 Notification authenticity\n\nA NIP-59 gift wrap authenticates the sender's signing pubkey but does **not** authenticate that the sender actually made the on-chain payment. A malicious sender could send a notification referencing a transaction made by someone else, or a transaction that does not pay the recipient at all. The on-chain verification step (§7.3) is the gate; the notification merely points the wallet at where to look. Wallets MUST NOT trust notification metadata without on-chain verification.\n\n### 9.3 Sighash discipline\n\nPer BIP-352, transactions paying silent-payment outputs MUST be signed with `SIGHASH_DEFAULT` or `SIGHASH_ALL`. `ANYONECANPAY` breaks the shared-secret derivation. Spending transactions (from silent-payment UTXOs) inherit the same constraint when they themselves contain silent-payment outputs (e.g. change paid to the change label). Wallets SHOULD apply this discipline uniformly.\n\n### 9.4 Coin selection and on-chain linkage\n\nConsolidating multiple silent-payment UTXOs in a single spending transaction links them on-chain as belonging to the same recipient, partially negating the per-payment privacy that BIP-352 grants on the receiving side. Wallets SHOULD support per-payment spends or, where consolidation is necessary, surface the privacy trade-off to the user.\n\n### 9.5 Change handling\n\nA spend transaction SHOULD route its change to its own silent-payment address using the BIP-352 change label (`m = 0`). The wallet will recover its own change on the next scan and will not need any special storage for it. Routing change to any non-SP address controlled by the user is permitted but creates a chain-analysis link between that address and the silent-payment cluster, partially negating the privacy benefit.\n\n### 9.6 Forward secrecy of NIP-44 ciphertext\n\nThe kind 10353 UTXO cache is encrypted with NIP-44, which inherits NIP-44's threat model: a future compromise of `nsec` retroactively decrypts all prior events. Wallets that want stronger forward secrecy for the UTXO cache SHOULD use only local storage and skip kind 10353 entirely.\n\n## 10. Backwards Compatibility\n\nThis NIP is purely additive. It defines a new derivation, a new address format use, and two new optional Nostr event kinds. It does not modify or depend on any existing Nostr event kind, address derivation, or wallet behavior.\n\nBecause the silent-payment address is publicly derivable from `npub`, no out-of-band capability negotiation or declaration event is required. Wallets that do not implement Part 1 simply will not scan for and credit incoming SP payments to their derived address until they upgrade.\n\nWallets that implement Part 1 but not Part 2 remain fully BIP-352-correct: they will discover every UTXO through chain scanning. Part 2 only affects latency-of-credit (kind 8352) and cross-device convenience (kind 10353).\n\n## 11. Test Vectors\n\n> **Status:** placeholders. To be filled in once a reference implementation lands. Each vector should include `nsec`, the corresponding `npub`, the derived `(t_scan, t_spend, bscan, bspend, Bscan, Bspend)`, the encoded `sp1q…` address, and for at least one example transaction: the inputs, the per-output tweak `tₖ`, the derived `dₖ`, the resulting Taproot output key, and a valid Schnorr signature spending it.\n\nPart 1:\n\n```\nTODO: additive derivation vector (npub → t_scan, t_spend, Bscan, Bspend)\nTODO: recipient derivation vector (nsec → d_npub → bscan, bspend; verify matches sender side)\nTODO: address-encoding vector\nTODO: send-side vector (build tx given only the recipient npub)\nTODO: receive-side vector (scan match given block data)\nTODO: spend-side vector (PSBT roundtrip)\n```\n\nPart 2:\n\n```\nTODO: kind 8352 vector (sender-side rumor + gift-wrap; recipient validates and credits)\nTODO: kind 10353 vector (encrypt/decrypt roundtrip of a sample UTXO set)\n```\n\n## 12. References\n\n- [BIP-340 — Schnorr Signatures for secp256k1](https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki)\n- [BIP-341 — Taproot (SegWit v1)](https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki)\n- [BIP-350 — Bech32m](https://github.com/bitcoin/bips/blob/master/bip-0350.mediawiki)\n- [BIP-352 — Silent Payments](https://github.com/bitcoin/bips/blob/master/bip-0352.mediawiki)\n- [BIP-174 — PSBT](https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki)\n- [BIP-371 — Taproot PSBT Fields](https://github.com/bitcoin/bips/blob/master/bip-0371.mediawiki)\n- [NIP-01 — Basic protocol](https://github.com/nostr-protocol/nips/blob/master/01.md)\n- [NIP-44 — Encrypted Payloads](https://github.com/nostr-protocol/nips/blob/master/44.md)\n- [NIP-59 — Gift Wrap](https://github.com/nostr-protocol/nips/blob/master/59.md)\n- [NIP-73 — External Content IDs](https://github.com/nostr-protocol/nips/blob/master/73.md)","sig":"5bac1bfb2302cda9c72aac051766cd7165742bd199f22d5d9e7c234bef898ddb3e635336d6a01eeb419faf9a46d94f631ab2359c812bd6c819a2f60bd56c9197"}