diff --git a/bip-spdescriptor.mediawiki b/bip-spdescriptor.mediawiki new file mode 100644 index 0000000000..2f2f22aed2 --- /dev/null +++ b/bip-spdescriptor.mediawiki @@ -0,0 +1,122 @@ +
+  BIP: spdescriptor
+  Layer: Applications
+  Title: Silent Payment Output Script Descriptors
+  Author: Craig Raw 
+  Comments-Summary: No comments yet.
+  Comments-URI: TBD
+  Status: Draft
+  Type: Informational
+  Created: 2025-12-03
+  License: BSD-2-Clause
+  Post-History: https://groups.google.com/g/bitcoindev/c/bP6ktUyCOJI
+  Requires: 44, 341, 350, 352, 380
+
+ +==Abstract== + +This document specifies sp() output script descriptors for silent payments. +sp() descriptors take silent payment key material and describe P2TR outputs when combined with sender input public keys as defined in BIP352. + +==Copyright== + +This BIP is licensed under the BSD 2-clause license. + +==Motivation== + +BIP352 defines silent payments, a protocol for static payment addresses without on-chain linkability. +This descriptor provides a standardized way to represent silent payment outputs within the output descriptor framework, enabling wallet interoperability and backup/recovery using existing descriptor-based infrastructure. + +==Specification== + +A new top level script expression is defined: sp(). + +===Key Expressions=== + +Two new key expression types are defined for use with sp() descriptors: + +====spscan==== + +The spscan key expression encodes the scan private key and spend public key. +It is a [https://github.com/bitcoin/bips/blob/master/bip-0350.mediawiki Bech32m] encoding of: +* The human-readable part "spscan" for mainnet, "tspscan" for testnets +* The data-part values: +** The character "q", to represent silent payments version 0 +** The payload: ser256(bscan) || serP(Bspend) + +====spspend==== + +The spspend key expression encodes both the scan and spend private keys. +It is a [https://github.com/bitcoin/bips/blob/master/bip-0350.mediawiki Bech32m] encoding of: +* The human-readable part "spspend" for mainnet, "tspspend" for testnets +* The data-part values: +** The character "q", to represent silent payments version 0 +** The payload: ser256(bscan) || ser256(bspend) + +Note: The serialization of ser256(p) and serP(P) follows the definition in BIP352. + +===sp()=== + +The sp(KEY) or sp(KEY,BIRTHDAY,LABEL,...) expression can only be used as a top level descriptor. + +sp(KEY) takes a single key expression as an argument, which must be either an spscan or spspend encoded key, optionally with key origin information. +If included, the key origin information should specify the derivation path to the account level as defined in BIP44. +When combined with sender input public keys, the descriptor produces P2TR output scripts describing silent payments made to wallets represented by the key expression. + +When using the minimal form sp(KEY): +* The birthday defaults to block height 842579 (May 8, 2024, when BIP352 was merged) +* Only the change label (m = 0) is assumed + +sp(KEY,BIRTHDAY,LABEL,...) takes: +* A key expression (first argument) +* A birthday as a positive integer representing a block height (second argument) +* Zero or more label integers (remaining arguments), where each label is a positive integer + +The birthday indicates the block height at which scanning should begin, and must be ≥ 842579. +Each label represents a label integer m used with the wallet, corresponding to Bm = Bspend + hashBIP0352/Label(ser256(bscan) || ser32(m))·G as defined in BIP352. +When labels are specified, only those specific label integers are scanned for (in addition to the change label m = 0 which is always scanned). + +The output scripts produced are BIP341 taproot outputs as specified in BIP352. + +==Examples== + +Valid descriptors: + +* sp(spscan1q...) - Minimal form with default birthday and change label only +* sp([deadbeef/352'/0'/0']spscan1q...,900000) - With key origin and custom birthday +* sp(spspend1q...,842579,1,2,3) - With birthday and labels 1, 2, 3 +* sp([deadbeef/352'/0'/0']spscan1q...,900000,1,5,10) - With key origin, birthday, and labels 1, 5, 10 + +Invalid descriptors: + +* sp() requires a key expression +* sp(xpub...) requires spscan or spspend encoded key +* sp(spscan1q...,abc) birthday must be a positive integer +* sh(sp(spscan1q...)) sp() is top level only +* wsh(sp(spscan1q...)) sp() is top level only + +==Usage Notes== + +The change label (m = 0) should always be scanned for, even when not explicitly listed. +This ensures compatibility across different wallet implementations and prevents loss of funds from change outputs. + +When recovering a wallet from a descriptor, scanning should begin at the specified birthday block height. +All labels specified in the descriptor must be scanned for when detecting payments. + +For watch-only wallets, use spscan encoding. +For full wallets that can both scan and spend, use spspend encoding. + +==Backwards Compatibility== + +sp() descriptors use the format and general operation specified in [[bip-0380.mediawiki|380]]. +As this is a wholly new descriptor, it is not compatible with any prior implementation. +The scripts produced are BIP341 taproot outputs, making them indistinguishable from other taproot outputs on-chain. + +==Reference Implementation== + +TBD + +==Test Vectors== + +TBD +