Nostr silent payment address
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
NIP-SP: Nostr-Derived Silent Payments
draft optional
This document defines how to send and receive BIP-352 silent payments 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.
Document structure
This NIP is organized in two independent parts.
- Part 1 — Bitcoin-side (§1–§6) is a self-contained Bitcoin protocol. Given an
npubit defines the BIP-352 address; given annsecit 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. - 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.
Cross-cutting concerns — security, compatibility, test vectors, references — follow as §9–§12.
⚠️ Critical Security Warning
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).
Consequences a wallet MUST internalize:
bscanandbspendMUST be treated with exactly the same care asnsec. They are not “view keys” in the conventional BIP-352 sense; for npub-derived wallets they are full identity-compromising secrets.- 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’snsec. This includes any remote indexer, hosted wallet, watch-only server, or “scan-for-me” relay that takesbscanas input. - 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
bscannever leaving the device. See §5.1. - Users who genuinely need a remote scanning service that ingests
bscanMUST use a separate BIP-352 wallet whose keys are independent of any Nostr identity — never an npub-derived wallet.
If your wallet UI exposes bscan to the user (e.g. for export), it MUST display the same warnings as for nsec export.
Motivation
Any 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.
A 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:
- There is no second secret to back up —
nsecremains the sole secret. - The recipient does not need to publish anything before they can be paid. Any sender that knows their
npubcan derive their silent-payment address and pay them. - The mapping is symmetric: the sender derives
(Bscan, Bspend)fromnpub; the recipient derives(bscan, bspend)fromnsec. Both arrive at the same key pair.
The 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).
Part 1 — Bitcoin-Side: Silent Payment Address Derivation and Spending
This 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.
1. Definitions
| Symbol | Meaning |
|---|---|
n |
Order of the secp256k1 curve |
G |
secp256k1 generator point |
nsec |
The recipient’s raw Nostr private key (32-byte scalar) |
npub |
The recipient’s Nostr public key (32-byte x-only, per BIP-340) |
P_npub |
The point lift_x(npub) — even-y by BIP-340 convention |
d_npub |
The BIP-340-normalized scalar such that d_npub · G = P_npub |
t_scan, t_spend |
Public per-identity additive tweaks (§2.1) |
bscan, bspend |
Derived BIP-352 scan/spend private keys |
Bscan, Bspend |
Derived BIP-352 scan/spend public keys |
tₖ |
BIP-352 per-output tweak for output index k |
m |
BIP-352 label scalar (0 ⇒ unlabeled / change) |
Pₖ |
The on-chain Taproot output key for output k |
H_tagged(tag, msg) |
BIP-340 tagged hash: SHA256(SHA256(tag) ‖ SHA256(tag) ‖ msg) |
All scalars are mod n unless stated otherwise. Public keys are serialized in compressed SEC1 form (33 bytes) except where x-only is required.
d_npub is the BIP-340-normalized form of nsec:
d_npub = nsec if (nsec · G) has even y
= n − nsec otherwise
This is the same normalization Nostr already applies when signing per BIP-340; no new state needs to be stored.
2. Key Derivation
2.1 Additive derivation from npub
Both the per-identity tweaks and the resulting scan/spend keys are derived as follows.
Tweaks (public; either side can compute):
t_scan = H_tagged("nostr-sp/scan", npub) mod n
t_spend = H_tagged("nostr-sp/spend", npub) mod n
Sender side (uses only npub):
Bscan = P_npub + t_scan · G
Bspend = P_npub + t_spend · G
Recipient side (uses nsec):
bscan = (d_npub + t_scan) mod n
bspend = (d_npub + t_spend) mod n
By construction, bscan · G = Bscan and bspend · G = Bspend, and both sides arrive at the same (Bscan, Bspend) pair without any communication.
The 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.
Because 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.
⚠️
bscanandbspendare NOT “view keys” — they are full identity-compromising secrets. Either one, combined with the user’s (publicly known)npub, lets an attacker recovernsec. 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.
2.2 Edge cases
- 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. - The change label
m = 0is always implicitly registered, per BIP-352. Recipients MUST scan for the change label.
3. Address Encoding
The 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).
Wallets 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.
3.1 Network scope
Implementations 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.
4. Sending
A sender follows the standard BIP-352 sending procedure. Given a recipient npub:
- Derive
(Bscan, Bspend)fromnpubper §2.1. No profile lookup, relay query, or declaration event is required. - 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.
- Compute the shared secret per BIP-352:
a = a₁ + a₂ + … + aₙ (sum of input private keys, mod n) A = a · G (sum of input public keys) input_hash = H_tagged("BIP0352/Inputs", outpointL ‖ A) shared = input_hash · a · Bscan - For each output to this recipient at index
k(per BIP-352 output ordering rules):tₖ = H_tagged("BIP0352/SharedSecret", serP(shared) ‖ ser32(k)) Pₖ = Bspend + tₖ · G (no label) = Bspend + (tₖ + m) · G (with label m) - Place a P2TR output
OP_1 <xonly(Pₖ)>in the transaction. - Sign with
SIGHASH_DEFAULTorSIGHASH_ALL.SIGHASH_ANYONECANPAYMUST NOT be used: the shared secret commits to the set of inputs and cannot be re-derived after input substitution.
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.
5. Receiving (BIP-352 Chain Scan)
The 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:
- Identify eligible inputs (per BIP-352) and compute
A = ΣAᵢandinput_hash. - Compute
shared = input_hash · bscan · A. - For each Taproot output in the transaction, iterate
k = 0, 1, …until no match:tₖ = H_tagged("BIP0352/SharedSecret", serP(shared) ‖ ser32(k)) P_check = Bspend + tₖ · G If xonly(P_check) matches the Taproot output key: → unlabeled UTXO found at this output, store (txid, vout, value, tₖ, m=0) Else: candidate_label = xonly(output) − P_check If candidate_label matches a precomputed label (incl. the change label): → labeled UTXO found, store (txid, vout, value, tₖ, m)
This 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.
5.1 Scanning architectures
⚠️ Because §2.1 is additive (not hardened),
bscancarries the full sensitivity ofnsec. Anyone givenbscanand the user’snpubcan computensec = (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 shipsbscanoff the user’s device is therefore equivalent to publishingnsecand is prohibited by this NIP.
Two — and only two — scanning architectures are permitted:
- 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.
- 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 privatebscan. The service never receivesbscanor any other private key material. Wallets choosing this mode MUST verify that the service protocol does not, at any point, ask forbscan.
A 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.
Users 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.
6. Spending
6.1 Output construction and signing chain
Each 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:
─── stage 1: BIP-340 parity-normalize the Nostr key ──────────────────
d_npub = nsec if (nsec · G) has even y
= n − nsec otherwise
─── stage 2: additive spend-key derivation (§2.1) ────────────────────
t_spend = H_tagged("nostr-sp/spend", npub) mod n
bspend = (d_npub + t_spend) mod n
─── stage 3: BIP-352 per-output tweak ────────────────────────────────
dₖ = (bspend + tₖ + m) mod n
─── stage 4: BIP-340 parity-normalize the output key ─────────────────
Pₖ = dₖ · G
d_sign = dₖ if Pₖ has even y
= n − dₖ otherwise
─── stage 5: sign ────────────────────────────────────────────────────
BIP-340 Schnorr signature with d_sign over the BIP-341 sighash.
NO further BIP-341 TapTweak is applied.
Exactly 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.
Conformance MUSTs:
- Implementations MUST NOT apply BIP-341 TapTweak when spending a NIP-SP output. The output key
Pₖis not an internal key. - Implementations MUST apply the stage-1 and stage-4 parity normalizations.
- Implementations MUST verify
xonly(dₖ · G)matches the on-chain output key before signing, as a guard against corrupted tweak data.
6.2 PSBT vocabulary
This 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.
Proprietary key encoding:
key = 0xFC ‖ compact_size(8) ‖ "nostr_sp" ‖ <subtype> ‖ <subkey_data>
value = <subtype-specific bytes>
Defined subtypes:
| Subtype | Subkey | Value | Meaning |
|---|---|---|---|
0x00 |
empty | 2 bytes: <version 0x00> ‖ <scheme 0x01> |
Marker. scheme = 0x01 ⇒ NIP-SP additive derivation (this NIP). |
0x01 |
empty | 32 bytes | Per-output BIP-352 tweak tₖ. |
0x02 |
empty | 32 bytes | Label scalar m. Absent ⇒ unlabeled (m = 0). |
0x03 |
empty | 32 bytes | Expected on-chain output key xonly(Pₖ). |
Conformance MUSTs and MUST NOTs at the input level:
- An input carrying subtype
0x00MUST also carry subtypes0x01and0x03. Subtype0x02is conditional. - An input carrying subtype
0x00MUST NOT carryPSBT_IN_TAP_INTERNAL_KEY(0x17). - An input carrying subtype
0x00MUST NOT carryPSBT_IN_TAP_MERKLE_ROOT(0x18). - An input carrying subtype
0x00MUST carryPSBT_IN_WITNESS_UTXO(0x01). - If
PSBT_IN_SIGHASH_TYPE(0x03) is present, its value MUST beSIGHASH_DEFAULT(0x00) orSIGHASH_ALL(0x01). - 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 forPSBT_IN_TAP_KEY_SIGis unchanged.
6.3 Signer behavior
A signer (whether local, NIP-07 extension, or NIP-46 bunker) that recognizes the "nostr_sp" prefix processes each marked input as follows:
for each input I in psbt:
if I has nostr_sp[0x00] with scheme = 0x01:
require I has WITNESS_UTXO
require I has nostr_sp[0x01]
require I has nostr_sp[0x03]
require I does NOT have TAP_INTERNAL_KEY
require I does NOT have TAP_MERKLE_ROOT
require sighash ∈ {DEFAULT, ALL} or absent
d_npub = nsec if (nsec · G) has even y else n − nsec
t_spend = H_tagged("nostr-sp/spend", npub) mod n
bspend = (d_npub + t_spend) mod n
tₖ = nostr_sp[0x01]
m = nostr_sp[0x02] if present else 0
dₖ = (bspend + tₖ + m) mod n
// Guard: derived key must match expected output key
if xonly(dₖ · G) ≠ nostr_sp[0x03]:
abort signing with explicit error
d_sign = dₖ if (dₖ · G) has even y else n − dₖ
sig = bip340_schnorr_sign(d_sign, taproot_sighash(psbt, I))
write PSBT_IN_TAP_KEY_SIG = sig
Signers 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).
6.4 Migration to a future BIP-352 PSBT standard
A 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.
Part 2 — Nostr Integration: Notifications and UTXO Cache
This 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.
- Kind 8352 (§7) — a sender-to-recipient receipt notification. Lets the recipient credit a specific UTXO without waiting for a chain scan.
- Kind 10353 (§8) — an encrypted self-published cache of the recipient’s own UTXO state. Enables cross-device sync and faster wallet recovery.
Both kinds use the recipient’s Nostr identity (the same npub that derives the SP address in Part 1) as the receiving / signing key.
7. Kind 8352 — Silent Payment Receipt Notification
Regular event used exclusively as the rumor inside a NIP-59 gift wrap. It MUST NOT be published directly to relays in the clear.
The kind number 8352 is 8000 + 352, encoding the BIP-352 base used throughout this NIP in the regular event range.
This 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.
{
"kind": 8352,
"pubkey": "<sender-pubkey>",
"content": "",
"tags": [
["i", "bitcoin:tx:<txid>"],
["vout", "<output-index>"],
["amount", "<sats>"],
["tweak", "<32-byte hex>"],
["label", "<32-byte hex>"],
["p", "<recipient-pubkey>"],
["alt", "Silent payment receipt: 50000 sats"]
]
}
7.1 Tags
| Tag | Required | Description |
|---|---|---|
i |
Yes | NIP-73 identifier bitcoin:tx:<txid>, with <txid> lowercase hex. |
vout |
Yes | Output index in the referenced transaction (decimal integer, ≥ 0). |
amount |
Yes | Output value in satoshis (decimal integer). |
tweak |
Yes | Per-output BIP-352 tweak tₖ as 64-char lowercase hex. |
label |
No | Label scalar m as 64-char lowercase hex. Omit for unlabeled outputs. |
p |
Yes | Recipient pubkey. MUST match the gift-wrap recipient. |
alt |
Yes | NIP-31 human-readable fallback. |
The content field SHOULD be an empty string. A future revision MAY permit a freeform message; for now, implementations SHOULD ignore it.
7.2 Sender behavior
After 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.
Emitting 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.
7.3 Recipient validation
A 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:
- Decrypt the gift wrap per NIP-59.
- Validate the rumor structure (§7.1).
- Fetch the referenced transaction from a Bitcoin data source.
- Independently verify that output
voutof the transaction paysPₖwhere:
(Pₖ = Bspend + (tweak + label) · Glabeldefaults to 0 if absent), and thatxonly(Pₖ)equals the Taproot output key oftx.vout[vout]. - Verify that the output’s
valuematches theamounttag (within tolerance of one satoshi, in case of integer conversion drift; SHOULD be exact). - If all checks pass, credit the UTXO to the wallet’s spendable set with
(txid, vout, value, tweak, label).
A 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.
7.4 Spam considerations
Because 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.
8. Kind 10353 — Encrypted Silent Payment UTXO Set (OPTIONAL)
Replaceable 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.
The kind number 10353 reflects: replaceable range (10000–19999) + an increment over the BIP-352 base used elsewhere in this NIP.
{
"kind": 10353,
"pubkey": "<recipient-pubkey>",
"content": "<NIP-44 ciphertext of a JSON-encoded tags array>",
"tags": [
["alt", "Encrypted silent payment UTXO set"]
]
}
8.1 Encryption
The 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.
8.2 Inner (encrypted) tag schema
After decryption, the plaintext is a JSON array of tags following the standard Nostr tag shape (each tag is an array of strings).
| Tag | Cardinality | Description |
|---|---|---|
scan_height |
Exactly one | Block height through which scanning has been completed, as a decimal string. |
utxo |
Zero or more | One per spendable UTXO. See UTXO Tag below. |
The 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.
UTXO Tag
Format: ["utxo", "<txid>", "<vout>", "<value>", "<tweak>"] for unlabeled UTXOs, or ["utxo", "<txid>", "<vout>", "<value>", "<tweak>", "<label>"] for labeled UTXOs.
| Index | Description |
|---|---|
| 0 | Tag name: "utxo" |
| 1 | Transaction ID, 64-char lowercase hex |
| 2 | Output index, decimal string (≥ 0) |
| 3 | Output value in satoshis, decimal string |
| 4 | Per-output BIP-352 tweak tₖ, 64-char lowercase hex |
| 5 | Label scalar m, 64-char lowercase hex. Omit for unlabeled outputs. |
Example decrypted plaintext:
[
["scan_height", "845231"],
["utxo", "abc123…", "2", "50000", "deadbeef…", "001f00…"],
["utxo", "def456…", "0", "12000", "cafebabe…"]
]
8.3 Recovery and persistence
The 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.
9. Security Considerations
9.1 Scan- or spend-key disclosure equals nsec disclosure
§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).
The full recovery, given a leaked bscan and the target npub:
t_scan = H_tagged("nostr-sp/scan", npub) mod n
d_npub = (bscan − t_scan) mod n ← BIP-340 signing key
nsec = d_npub or n − d_npub ← original raw nsec (one of two parities)
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.
Operationally, this means:
bscanandbspendMUST be treated with exactly the same care asnsec. They MUST NOT be transmitted to, stored on, or processed by any third party.- Remote “full scanning services” — where the user uploads
bscanand 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 exportbscanorbspendto a remote endpoint. - 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.
- Backup and key-export UI MUST display the same warnings for
bscanandbspendas it does fornsec.
Users 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.
9.2 Notification authenticity
A 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.
9.3 Sighash discipline
Per 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.
9.4 Coin selection and on-chain linkage
Consolidating 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.
9.5 Change handling
A 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.
9.6 Forward secrecy of NIP-44 ciphertext
The 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.
10. Backwards Compatibility
This 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.
Because 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.
Wallets 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).
11. Test Vectors
Status: placeholders. To be filled in once a reference implementation lands. Each vector should include
nsec, the correspondingnpub, the derived(t_scan, t_spend, bscan, bspend, Bscan, Bspend), the encodedsp1q…address, and for at least one example transaction: the inputs, the per-output tweaktₖ, the deriveddₖ, the resulting Taproot output key, and a valid Schnorr signature spending it.
Part 1:
TODO: additive derivation vector (npub → t_scan, t_spend, Bscan, Bspend)
TODO: recipient derivation vector (nsec → d_npub → bscan, bspend; verify matches sender side)
TODO: address-encoding vector
TODO: send-side vector (build tx given only the recipient npub)
TODO: receive-side vector (scan match given block data)
TODO: spend-side vector (PSBT roundtrip)
Part 2:
TODO: kind 8352 vector (sender-side rumor + gift-wrap; recipient validates and credits)
TODO: kind 10353 vector (encrypt/decrypt roundtrip of a sample UTXO set)
Looking for comments…
Searching Nostr relays. This may take a moment the first time this article is opened.
Looking for comments…
Searching Nostr relays. This may take a moment the first time this article is opened.