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

BIP-47 分层确定性钱包的可复用支付码

定义可公开张贴的“支付码”:一个码长期复用,付款方却能用 ECDH 为每笔付款算出互不关联的一次性地址,避免复用同一收款地址的隐私与安全损失。本 BIP 目前处于已部署状态。

一、档案卡

档案卡
编号BIP-47
标题Reusable Payment Codes for Hierarchical Deterministic Wallets
作者Justus Ranvier <[email protected]>
指定日期2015-04-24
层级Applications(应用)
类型Specification(规范类)
状态Deployed(已部署)
阅读版https://bips.dev/47
官方原文https://github.com/bitcoin/bips/blob/master/bip-0047.mediawiki

二、放在哪一族

BIP-47 是 HD 钱包派生路径族的一员,是 BIP-43 的一个具体应用(purpose = 47’),并自述为对实现 BIP-44 的 HD 钱包的补充:支付码身份与 BIP-44 账户一一配对,经支付码收到的款与经 BIP-44 地址收到的款等同,花费时可以同作一笔交易的输入。

同族关系:

BIP-32:派生算法底座

BIP-43:用途字段规则

BIP-44:多账户层级,本篇的 identity 层与它的 account 层对应

它在原文状态一节另有说明:就兼容实现第 1、2 版支付码的钱包而言,本 BIP 可视为定稿;但支付码规范今后的发展(第 3 版及以上)不再经 BIP 流程发布,应查 Open Bitcoin Privacy Project 的 RFC 仓库。

三、它要解决什么

商家与个人常常需要一个能长期公开、与现实身份绑定的收款入口。直接复用同一个 P2PKH 地址收款,代价是安全与隐私尽失:所有付款在链上彼此关联,任何人都能看到这个身份收了多少、与谁往来。

一次性地址隐私好,却无法“张贴”:你不能把下个月才生成的地址印在今天的名片上。

BIP-47 要的是两者兼得。原文动机说:支付码在商家—顾客交互中给交易加上身份信息,同时保护用户隐私;它把 Darkwallet 式隐身地址的隐私收益带给 SPV 客户端,无需可信全节点协助,并大幅减少对区块链存储的依赖。

办法是把“公开的收款标识”与“实际收款地址”分开:公开的是支付码(一组扩展公钥加元数据),实际地址由付款方在付款时用 ECDH 临时算出,每笔都不同,且只有收付双方能算出彼此的关联。

四、规则本身

4.1 三层路径与三类子密钥

m / purpose' / coin_type' / identity'

purpose 为 47’(0x8000002F),coin_type 与 BIP-44 同义,identity 层等价于 BIP-44 的 account 层。前三层用硬化派生;identity 的扩展公钥加上表示一节的元数据,构成一个“支付码”。

identity 之下派生的子密钥分三种用途(除注明外均为公开派生):

4.2 支付码的表示

二进制序列化共 80 字节:第 0 字节为版本号;第 1 字节为特征位(bit 0 表示 Bitmessage 通知,其余保留);第 2 字节为 sign(0x02 或 0x03);第 3–34 字节为公钥的 x 值;第 35–66 字节为链码;第 67–79 字节保留、全零。

展示给用户时应使用 Base58Check 编码,版本字节为 0x47,序列化结果以字母 “P” 开头。

4.3 两个版本

原文建议:具备布隆过滤能力的钱包应生成第 2 版支付码;只有缺乏该能力的钱包才推荐第 1 版。不同版本的支付码原则上互通:付款方版本与收款方版本不同,仍可互相收付,通知流程按收款方支付码的版本走。

4.4 通知交易:先把付款方的支付码私下递过去

收款方要能算出付款方为自己生成的临时地址,前提是先知道对方的支付码。第一次付款前,付款方必须发一笔通知交易:

  1. 向收款方的通知地址付一小笔比特币;所选输入不得容易与付款方自己的通知地址关联;
  2. 付款方用指定输入(designated input)对应的私钥 a 与收款方通知地址公钥 B 做 ECDH,得秘密点 S = aB,再以其 x 值与所花费的 outpoint 计算 64 字节致盲因子 s = HMAC-SHA512;
  3. 把自己的支付码序列化后,用 s 的前 32 字节异或 x 值、后 32 字节异或链码,使其只有收款方能读;
  4. 把这 80 字节放进交易的 OP_RETURN 输出。

收款方监听通知地址:收到带 80 字节 OP_RETURN 的交易后,用自己的通知私钥 b 与指定公钥 A 算出同一个 S 与 s,反异或还原出对方支付码,并校验 x 值是否属于 secp256k1 群。

通知地址收到的币要特殊处理,以免隐私泄漏:其金额不得计入用户可花费余额显示;这些输出不得用作任何涉及本用户支付码 ECDH 计算的交易的输入;可以先经混币服务,或以 dust-b-gone 一类方式捐给矿工。

4.5 正式付款:每笔一个临时地址

通知完成后,付款方每次付款都为收款方派生一个全新的 P2PKH 地址:取自己支付码的第 0 个私钥 a,与收款方支付码下“下一个未用”的公钥 B(索引按这一对收付双方的上下文计,不是全局计数)做 ECDH,得 S;标量共享秘密 s = SHA256(Sx)(若 s 不在 secp256k1 群内,则换下一个索引重试);临时公钥 B’ = B + sG,由它生成收款地址。

收款方自收到通知交易起,就用前瞻窗口(lookahead window)批量试算这些临时地址监听到账,并以 b’ = b + s 算出每个临时地址的私钥。通知只需一次,之后同一付款方可发送多达 2³² 笔付款。

退款顺理成章:收款方在通知过程中已得知付款方的支付码,可用同样流程回付;但他第一次向对方付款前,仍必须先发一笔通知交易,哪怕此前收到过对方的款。

4.6 其他要点

五、谁必须动

普通持币人(收款方):对外只公开支付码,不要再长期复用单个地址收款。要理解第一次收款前对方会发一笔小额通知交易,那笔钱不在可花费余额里显示是规范要求,不是丢币。

付款方:第一次付款前必须完成通知流程;每笔付款必须取“下一个未用”索引派生新地址,不得把同一临时地址付两次。

钱包开发者:工作量最重——支付码的生成与序列化、通知交易的构造与识别、ECDH 致盲与还原、前瞻窗口扫描、通知输出的隔离处理、种子恢复时的全链重扫,都要实现。还要在元数据里保存“已通知过的支付码清单与各自的下一索引”,并知道这部分元数据无法从种子完全恢复。

全节点与矿工:无特殊义务。通知交易与临时地址付款在链上都是普通交易形态。

共识层:无变化。

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

状态为 Deployed。按原文自己的状态说明,它的定稿范围限于第 1、2 版支付码的兼容性;第 3 版及以后的规范改到 Open Bitcoin Privacy Project 的 RFC 仓库发布,不再走 BIP 流程。因此,评价或实现 BIP-47 时,要以“v1/v2 的最终文本在此,后续演进在别处”为前提。

常见误读:

“支付码就是一个可以反复收款的地址。”

不对。支付码不是地址,它是一组扩展公钥加元数据。真正收款的地址每笔付款都由 ECDH 临时算出、互不相同;若把支付码误当地址直接反复打款,等于退回到地址复用,本篇要避免的隐私损失会原样发生。

七、延伸

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