协议演进 / BIP/比特币改进提案 / BIP-46
BIP-46
为时间锁定的保真保证金定统一派生方案:地址与锁定月份都由索引推出,用户只需备份种子,不必另行记录每个时间锁的日期。本 BIP 目前处于草案状态。
一、档案卡
| 编号 | BIP-46 |
|---|---|
| 标题 | Address Scheme for Timelocked Fidelity Bonds |
| 作者 | Chris Belcher <[email protected]>, Thebora Kompanioni <[email protected]> |
| 指定日期 | 2022-04-01 |
| 讨论日期 | 2022-05-01 |
| 层级 | Applications(应用) |
| 类型 | Specification(规范类) |
| 状态 | Draft(草案) |
| 阅读版 | https://bips.dev/46 |
| 官方原文 | https://github.com/bitcoin/bips/blob/master/bip-0046.mediawiki |
二、放在哪一族
BIP-46 挂在 HD 钱包派生路径族之下,但服务的对象很专门:保真保证金(fidelity bonds)的时间锁定地址。它不另造派生体系,而是借用现成账户形态改一个参数:
- 派生算法底座是 BIP-32;
- 账户结构与 BIP-84 一脉相同,原文说它大体沿用了 BIP-49、BIP-84、BIP-86 的做法以便实现,只是把 change 层取值为 2;
- 时间锁本身靠 BIP-65 的 OP_CHECKLOCKTIMEVERIFY 实现;
- 与 BIP-44 的关系是“账户骨架同源”,但用途完全不同:一个管日常多账户收款,一个管锁仓凭证。
原文还自述:这套标准已经在 JoinMarket 中实现并部署,本 BIP 更多是把既有做法记录成文(原文用语是 documenting something which already exists),因此评审期可改动的空间有限。
三、它要解决什么
3.1 什么是保真保证金
某些去中心化匿名协议需要抵御女巫攻击(sybil attack):让同一个攻击者廉价地伪造大量身份。保真保证金的办法是故意牺牲比特币的价值来让一个密码学身份变得昂贵——具体做法是把币用 OP_CHECKLOCKTIMEVERIFY 锁进一个时间锁定地址,牺牲的是资金的时间价值。
这种牺牲必须能向第三方证明:出示该 UTXO 的 outpoint、地址的赎回脚本,以及用赎回脚本中公钥对应的私钥做出的签名即可。牺牲的价值是客观、可核验、不可伪造的量(原文类比工作量证明)。原文举的实例是 JoinMarket:其用保真保证金抵御女巫攻击,写作时累计锁定超过 600 BTC,有的锁定多年;原文估算,攻击者若要解混所有 CoinJoin,需锁定超过 10 万 BTC 数年。
3.2 为什么需要统一派生方案
如果每家钱包各定一套时间锁地址的生成方法,用户备份保证金时除了种子还要逐个记录时间锁数值,丢了记录就可能找不回锁仓。
统一方案的目标是:用户只需保存 HD 种子加“用过本 BIP”这一事实,不需要备份任何时间锁数值——因为时间锁可以由地址索引算出来。同时,私钥可以放在冷存储里:系统在没有热钱包风险的前提下获得抗女巫能力。
3.3 冷存储下的证书机制
保证金放在冷存储时,日常要证明“我拥有这笔保证金”不能每次都动冷私钥。原文为此设了一个中间密钥对,称证书(certificate):
UTXO key ---signs---> certificate ---signs---> endpoint
端点(endpoint)可以是 IRC 昵称或 Tor onion 主机名。证书密钥对可以放在线使用,证明保证金归属。原文也提醒:即便热钱包私钥被盗,时间锁定地址里的币仍然安全,但盗窃者能在锁定期届满前冒充这笔保证金的身份。
四、规则本身
4.1 公钥派生:固定路径,change = 2
由根账户派生公钥的路径与 BIP-84 的账户结构类似,但 change 固定为 2:
m / 84' / 0' / 0' / 2 / index
沿此路径派生的密钥,原文称为 derived_key。index 从 0 起顺序编号,上限 959(含):一个 BIP32 主密钥最多派生 960 个地址。
这里没有间隔上限(gap limit)的概念:钱包必须始终生成全部 960 个地址,并逐一检查其余额与历史。
4.2 时间锁由索引推出
每个地址的时间锁是一个 Unix 时间,恒为某月第一天的第一秒。index 按月向上数:从 2020 年 1 月起,到 2099 年 12 月止,80 年 × 每年 12 个月,正好 960 个时间锁。换算式为:
year = 2020 + index // 12
month = 1 + index % 12
原文提醒:在 32 位系统上要小心 2038 年问题。设计意图(Rationale)正是让时间锁集合足够小且固定,用户无需记忆或存储任何日期。
4.3 地址构造:P2WSH 时间锁脚本
由派生公钥与时间锁组装见证脚本,资金在时间锁到期前不可动,到期后还需 derived_key 的签名:
witnessScript: <timelock> OP_CHECKLOCKTIMEVERIFY OP_DROP <derived_key> OP_CHECKSIG
该见证脚本经 SHA256 哈希得到 32 字节见证程序,输出脚本为 P2WSH 形式(0 <32-byte-hash>),scriptSig 为空。
4.4 证书消息签名
实现者应支持对 ASCII 消息签名。证书消息格式为:
"fidelity-bond-cert" || "|" || cert_pubkey || "|" || cert_expiry
其中 cert_expiry 是证书失效的 2016 区块周期序号:例如取 330,则证书在区块高度 665280(= 330 × 2016)之后失效。设有效期的目的,是证书密钥对一旦泄露,攻击者冒充保证金的时间也被限制住。
签名流程与许多钱包已有的“Sign Message”功能相同:消息前拼上 0x18 || "Bitcoin Signed Message:\n" 与长度字节,再用 derived_key 的私钥签名。因此多数实现无需为本节单写代码,用户把证书消息粘贴进现成的签名界面即可。原文强调:实现创建证书签名最重要,验证证书签名次之。
4.5 向后兼容:有意不兼容
原文在 Backwards Compatibility 一节明说:本 BIP 按设计不向后兼容(理由援引 BIP-49 的 Considerations)。不兼容的钱包根本发现不了保真保证金,用户会立刻察觉不对——以“明显报错”代替“静默漏检”。
原文附有完整的测试向量(助记词、根密钥、若干索引对应的私钥/公钥/时间锁/赎回脚本/地址与证书签名示例),实现者可据以自检。
五、谁必须动
普通持币人:备份负担被压到最低——种子短语加上“用过本 BIP”即可,不必保存时间锁日期表。但要知道恢复时钱包须全量扫描 960 个地址,且不支持本方案的钱包看不到这笔锁仓。
钱包开发者:需实现固定路径 m / 84' / 0' / 0' / 2 / index、由索引推时间锁、P2WSH 时间锁脚本构造、全量 960 地址检查,以及用时间锁地址私钥签证书消息的能力。
JoinMarket 等协议的使用者与验证方:按证书链核验保证金归属:UTXO 与赎回脚本证明牺牲,证书签名把在线身份与冷存储的保证金绑在一起。
全节点与矿工、共识层:无变化。时间锁用的是既有的 OP_CHECKLOCKTIMEVERIFY 共识规则,本 BIP 只约定钱包侧的派生与签名方式。
六、现在算不算数,以及一个常见误读
状态为 Draft(草案)。但“草案”在这里不等于“没人用”:原文自述这套方案已经在 JoinMarket 实现并部署,本文档的角色是把线上系统的既有做法成文归档,因此状态与落地程度要分开看。它算不算数,取决于具体协议与钱包是否按此派生——对 JoinMarket 的保证金场景,它就是现行做法的说明书。
常见误读:
“用了 BIP-46,恢复钱包时还得把每个时间锁日期另外备份好。”
不对。这正是本篇要消灭的负担:时间锁由地址索引按固定公式推出,全集只有 2020 年 1 月至 2099 年 12 月的 960 个月份。用户只需种子与“用过本 BIP”这一信息,钱包全量生成 960 个地址逐一核对即可。
七、延伸
- 阅读版:https://bips.dev/46
- 官方原文:https://github.com/bitcoin/bips/blob/master/bip-0046.mediawiki
- 相关:BIP-32 分层确定性钱包、BIP-65 OP_CHECKLOCKTIMEVERIFY、BIP-44 多账户层级、BIP-49 P2WPKH 嵌套 P2SH 派生、BIP-84 P2WPKH 派生、BIP-86 P2TR 密钥派生
- 仓库总目录:https://github.com/bitcoin/bips
