协议演进 / BIP/比特币改进提案 / BIP-352

BIP-352 静默支付

收款方只公开一个静态的静默支付地址,付款方为每笔付款算出独一无二的 Taproot 输出,无需交互、无需链上通知,链上也看不出关联。本 BIP 目前处于已完成状态。

一、档案卡

档案卡
编号BIP-352
标题Silent Payments
作者josibake <[email protected]>, Ruben Somsen <[email protected]>, Sebastian Falbesoner <[email protected]>
指定日期2023-03-09
讨论日期2022-03-13
层级Applications(应用)
类型Specification(规范类)
状态Complete(已完成)
依赖BIP-340, BIP-341, BIP-350
阅读版https://bips.dev/352
官方原文https://github.com/bitcoin/bips/blob/master/bip-0352.mediawiki

二、放在哪一族

隐私收款与可复用地址族。它与 BIP-351(Private Payments)解决同一个问题——收款标识公开分享时如何不丢隐私——但路线更彻底:连通知交易都不要。

它站在 Taproot 的肩膀上:

BIP-340:Schnorr 签名,密钥运算的基础

BIP-341:Taproot,所有静默支付收款输出最终都编码为 BIP-341 的 Taproot 输出

BIP-350:Bech32m 编码,静默支付地址用它编码

BIP-47 与 BIP-351:早期的支付码路线,都需要链上通知;BIP-352 把通知环节整个去掉

后续配套还有用 PSBT 发送与花费静默支付输出的提案(见本站 BIP 索引中 BIP-375、BIP-376),以及静默支付输出脚本描述符等钱包侧标准。

三、它要解决什么

保持隐私的基本要求是每笔交易用新地址。这通常需要收款方先交互交付一个新地址、一批地址,或者像 xpub 那样交付一个能按需生成地址的方法。但交互常常做不到、也不想要:捐赠页、公开收款页、离线场景,都需要一个能长期贴出来的静态标识。

以往用静态标识的协议靠区块链发通知来补交互,代价是:一次性付款成本变高、链上留下显眼足迹、通知元数据可能暴露发送方与接收方,而且收款方能把同一发送方的多笔付款全部关联起来,伤发送方隐私。

BIP-352 的目标清单在原文里列得很明确:不增加交易体积与成本;交易看起来与普通比特币交易无异、无法被分辨;外部观察者无法把交易关联到静默支付地址;无需发送方与接收方交互;同一发送方的多笔付款互不关联;每笔付款去往唯一地址、不会意外复用;支持付款标签;能用现有助记词或描述符方式备份恢复;扫描与花费职责可分离;兼容 CoinJoin 等协作交易;支持轻客户端;协议可升级。

代价也写明了:收款钱包必须扫描区块链才能发现付款。全节点做这件事可行,轻客户端则吃力——原文附录 A 把轻客户端支持列为开放研究问题,给出了若干设想,但不属于本规范正文。

四、规则本身

4.1 地址:一对公钥,Bech32m 编码

静默支付地址(version 0)装的不是一个公钥,而是两个:扫描公钥 B_scan 与花费公钥 B_spend,各 33 字节压缩格式,拼接成 66 字节后用 Bech32m 编码。主网人类可读前缀为 sp,测试网为 tsp,版本字符为 q(即地址以 sp1q 开头)。原文特意说明:这类地址至少 116 个字符,建议实现把长度上限放宽到 1023 字符,为未来版本留余地。

分两把钥匙的原因是安全分工:扫描需要私钥在线,b_scan 可以放在联网设备上找钱;b_spend 可以留在离线冷存储里,只在花钱时用。扫描方只有 B_spend 的公钥,算得出收款输出、却花不了。

用 BIP32 派生时,purpose 定为 352,且扫描与花费密钥都必须用硬化派生:扫描密钥路径 m / 352' / coin_type' / account' / 1' / 0,花费密钥路径 m / 352' / coin_type' / account' / 0' / 0。硬化派生保证导出扫描私钥不会牵连主密钥与花费私钥。

4.2 付款方怎么算出收款输出

简化情形:Alice 用自己的输入私钥 a、Bob 的扫描公钥做 ECDH,P = B_spend + hash(a·B_scan)·G,把 P 编码为一个 Taproot 输出付款。Bob 用 b_scan 与 Alice 输入的公钥 A 算出同一个值(因 a·B_scan == b_scan·A),扫描时对上即知是自己的钱;花费私钥是 b_spend 加上那段哈希微调。

规范在此之上加了三层处理:

合资格参与共享秘密推导的输入类型只有四种:P2TR、P2WPKH、P2SH-P2WPKH、P2PKH,且只许 X-only 与压缩公钥。带条件分支或多公钥的输入(如 CHECKMULTISIG)被排除,因为在协作交易中会引入可塑性。签名哈希类型不得使用 SIGHASH_ANYONECANPAY,否则签名后输入被改动,收款方就再也算不出共享秘密。

有个防滥用上限:共享同一扫描公钥的一组收款地址最多 K_max = 2323 个,超限即失败,以免恶意构造的交易让扫描方的计算量爆炸。

4.3 标签(Labels)

Bob 想区分同一地址收到的不同来源付款,不必公开多个地址(那会成倍增加扫描量),可以用整数 m 给花费公钥贴标签:B_m = B_spend + hash(BIP0352/Label, b_scan || m)·G,m 从 1 起。公布出去的各标签地址共享同一个 B_scan,所以原文提醒:外人能看出这些标签地址属于同一主体,标签是用来分辨“钱从哪来”,不是用来分隔身份。

m = 0 被保留作找零标签:钱包给自己找零时打上它,恢复钱包时就能认出哪些输出是自己的找零。每个收款钱包恢复时都至少应扫描找零标签,以保证跨钱包兼容。标签信息应写入备份;若不确定是否用过标签,恢复时建议预计算大量标签(原文举例 10 万个)再扫描。

4.4 收款方扫描与花费

一笔交易同时满足三个条件才需要扫描:至少含一个 BIP-341 Taproot 输出;至少有一个合资格类型的输入;不花费 SegWit 版本大于 1 的输出(为未来版本留干净的升级路径)。

扫描时 Bob 汇总全部合资格输入公钥、算 input_hash 与共享秘密,从 k = 0 起逐个生成候选输出与交易中的 Taproot 输出比对,并顺带检查标签(含找零标签)。原文特别警告:是否继续扫描(k 递增)必须以密码学上是否命中为准,不能以钱包策略为准——哪怕命中的输出因是粉尘而被钱包过滤掉,也必须继续试下一个 k,否则会漏掉同一发送方的后续输出。

花费时私钥为 (b_spend + t_k + 标签微调) mod n,按普通 BIP-341 输出花费即可。链上看,它就是一笔普通的 Taproot 花费。

4.5 向后兼容

静默支付引入了新地址格式与新的发送协议,与未实现本协议的旧钱包不兼容:旧钱包既不能向静默支付地址付款,也不能识别收到的静默支付输出。未来版本通过地址中的版本位升级,并要求尽可能让旧钱包能向新版本地址付款;破坏兼容的改动应另立新 BIP。

五、谁必须动

普通持币人:收款只多一件事——保管好种子,恢复钱包时要从钱包“生日”起重新扫描链上数据才能找回全部静默支付收款。付款给静默支付地址则与付普通地址无异,前提是钱包已实现本协议。

钱包开发者(发送方):选币时至少要有一个合资格输入、排除 SegWit 版本大于 1 的输入、Taproot 输入须能拿到密钥路径私钥(内部钥匙为 NUMS 点 H 的除外);按输入之和计算共享秘密;RBF 改动输入后必须重新推导静默支付输出。

钱包开发者(接收方):负担最重——实现全链扫描或接入能提供微调数据的服务、标签管理、找零标签扫描与备份恢复流程。原文附有 JSON 测试向量与 Python 参考实现,发送与接收两侧可分别对照测试。

全节点与矿工:无任何改动。静默支付交易就是普通交易,共识与转发规则都不变。

共识层:不涉及。本 BIP 是应用层协议。

六、现在算不算数,以及一个常见误读

状态为 Complete(已完成):指规范文本已经定稿完整,可以照着实现,而不是说全网或全部钱包都已采用。它不需要激活、不需要软分叉,算不算数取决于各个钱包是否实现发送与接收支持;实现进度参差,原文没有给出采用统计,本页也不臆测。

常见误读:“静默支付地址是一个可以反复用的链上地址,用了它就等于地址复用。”不对。sp1q... 地址本身从不上链,它只是扫描与花费两把公钥的编码;每笔付款真正落到链上的,是付款方用 ECDH 为这一笔单独算出的 Taproot 输出,每笔都不同,外部观察者无法把它们关联回那个公开地址。被“复用”的只有链下分享的标识,不是链上地址。

七、延伸

本页内容整理自 bitcoin/bips 仓库与 bips.dev,事实已与仓库核对;解读仅供学习参考。