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

BIP-83 动态分层确定性密钥树

不再预先定死 HD 钱包的路径结构:每层留 0 号子节点继续向下分叉,让密钥树可以按应用需要无限往深处长。本 BIP 目前处于已关闭状态。

一、档案卡

档案卡
编号BIP-83
标题Dynamic Hierarchical Deterministic Key Trees
作者Eric Lombrozo <[email protected]>
指定日期2015-11-16
层级Applications(应用)
类型Specification(规范类)
状态Closed(已关闭)
阅读版https://bips.dev/83
官方原文https://github.com/bitcoin/bips/blob/master/bip-0083.mediawiki

二、放在哪一族

分层确定性(HD)钱包族里的一份“反定式”提案。同族的主流做法是预先定死路径结构:BIP-32 给派生算法,BIP-43 占用第一层作 purpose,BIP-44 定多账户层级,BIP-45、BIP-49、BIP-80、BIP-81、BIP-84 等各自再占一个 purpose 编号、定一套固定层级。BIP-83 恰恰质疑这个方向本身,主张路径不应被任何一份规范提前写死。

它的参考文献只有两项:BIP-32 与闪电网络白皮书——后者对应它给出的双向支付通道用例(见 4.4)。理解本篇,关键是把它读成对整个路径标准化路线的不同意见,而不是又一份路径方案。

三、它要解决什么

原文的动机说得很直白:此前诸多提案(点名 BIP-32、BIP-44、BIP-45)都试图为 HD 钱包强加一套预先定好的结构以求互操作,但全都一上来就把结构钉死,既不给“动态新建带不同语义的层级”留余地,也不给“使用不同结构的应用之间如何映射”留余地。

现实中的需求是会生长的:今天只有账户与找零两层,明天可能要给子账户、给支付通道、给还没想到的新应用再分层。若每一层语义都要先立一份新 BIP、占一个新编号才能用,树的生长就被规范流程卡住了。本 BIP 的主张因此是换一个设计目标:不预先强加结构,而是把派生方式设计成可以任意、无限地继续扩展层级,让结构随应用生长。

四、规则本身

4.1 出发点:一个节点不能既当文件又当目录

原文先指出 BIP-32 的一个内在约束:树上每个节点要么用来派生子节点,要么用作 ECDSA 签名密钥,二者只能选其一——一旦拿一个节点当了签名密钥,就不能再从它派生孩子。用文件系统打比方:每个节点要么是文件、要么是目录,永不能兼任。

由此还推出一条实践忌讳:同一层里不要混放签名密钥与父节点,因为子节点的位置必须可预测。换句话说,只要某一层被定为“签名密钥层”,这一支就再也无法往下嵌套更深的层级——固定结构之所以僵,根源在此。

4.2 核心规则:每层预留 0 号给继续分叉

解法只有一条,却能递归地用下去:在每一个层级,都预留索引为 0 的那个孩子专门用于继续向下嵌套;签名密钥的父节点则用索引 1 到 MAX_INDEX(2^31 − 1)的孩子作为签名密钥。硬化与非硬化派生都可以用。

设 p 为某个签名密钥父节点、k 为大于 0 的索引,则它的签名子密钥是:

p / k        (k > 0)

要新建子层级,则派生嵌套节点:

p / 0 / n    (n ≥ 0)

得到的每个新节点,又各自可以拥有自己的签名子密钥,并再次预留自己的 0 号孩子给更深的嵌套。如此递归,深度不限。

原文也解释了为何偏选 0 号而不是别的保留位:也可以用 MAX_INDEX 来留作嵌套,但那样语义更怪异,且将来若想把索引扩展到 31 位以上会出问题;留 0 最简单,剩下的正索引正好是连续编号的签名密钥。原文同时点出与 BIP-32 建议的差异:BIP-32 本来建议预留 0 号孩子来派生签名密钥,本 BIP 则把 0 号改作子层级派生之用——因为每个密钥的父路径本来就会被显式写明,真正不可不预留的,是“往下再分一层”的能力。

4.3 记号:用 // 表示嵌套

原文提议一套速记:

p // n    代替 p / 0 / n
p //' n   代替 p / 0' / n(硬化版)

双斜杠一眼表示“在这里下潜一层”,让深路径读起来仍能分清哪些步是选签名密钥、哪些步是开新层级。

4.4 映射与用例

与固定结构最决裂的一笔在映射(Mappings)一节:原文拒绝预先规定哪条路径对应哪种用途(如外部收款还是内部找零)。不同应用可以自行指定任意父节点与派生路径;需要互操作时,应用之间交换这些映射即可。若某些映射将来流行起来,应用界面再把它们做成快捷方式或默认值。设计被刻意做成完全开放式(open-ended)。

原文给了两个用例。其一是账户层级:设索引 k > 0、父节点索引 n ≥ 0,从主种子 m 出发,默认账户的找零/内部输出用 m / 1 / k、发票/外部输出用 m / 2 / k;子账户 a_n 取 a_n = m // n,其下找零与发票再分别用 a_n / 1 / k 与 a_n / 2 / k;依此可无限建子账户。其二是双向支付通道:撤销旧承诺的办法是向对方揭示一个秘密,使对方能在旧承诺上链时取走通道资金;有了任意深度的嵌套,就能构造任意深的决策树,并靠揭示某个父节点一次性撤销整整一支——该父节点派生出的全部子密钥随之作废。

五、谁必须动

普通持币人:无直接动作。本 BIP 不改变任何现用钱包的既有路径;若某个应用采纳它,用户感知到的只是该应用内部账户与子账户的组织方式。

钱包与应用开发者:这是唯一的动作方,且动作是“采纳一种派生纪律”而非实现某个固定结构:每层保留 0 号作嵌套口、1 起作签名密钥、路径与用途的映射由应用自定并在需要互操作时与对方交换。代价也要认清:放弃固定结构,等于放弃“凭一条路径就能认出钱包方案”的现成互操作——恢复文件时若不知道原应用用的映射,单靠种子推不出原来的账户划分。

全节点与矿工:不涉及。密钥派生全在钱包内部完成,链上只见公钥、地址与签名。

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

状态为 Closed(已关闭)。它没有成为 HD 钱包的现行组织方式;现实中胜出的仍是它所批评的那条路——以 purpose 编号区分方案、用固定路径换互操作(如 BIP-44、BIP-49、BIP-84、BIP-86 一脉)。按状态读,本 BIP 应被当作一份留档的不同意见:问题提得真切(固定结构无法生长),给出的保留 0 号方案也完整自洽,但社区最终没有沿这条路走。原文没有记录关闭理由,本页不替它补。

常见误读:

“BIP-83 又是给钱包加一层的新路径标准,照着改路径就行。”

不对。它恰恰反对再多立一套固定路径:全文没有规定任何一层必须表示什么,只规定一条可递归的留位纪律,并明说用途映射应由各应用自定、事后交换。把它当成 BIP-44 那样的填空表来“照着改”,就读反了它的主张。

七、延伸

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