JSRUN 用代码说话

HD钱包

编辑教程

HD钱包

在比特币的链上,实际上并没有账户的概念,某个用户持有的比特币,实际上是其控制的一组UTXO,而这些UTXO可能是相同的地址(对应相同的私钥),也可能是不同的地址(对应不同的私钥)。

出于保护隐私的目的,同一用户如果控制的UTXO其地址都是不同的,那么很难从地址获知某个用户的比特币持币总额。但是,管理一组成千上万的地址,意味着管理成千上万的私钥,管理起来非常麻烦。

能不能只用一个私钥管理成千上万个地址?实际上是可以的。虽然椭圆曲线算法决定了一个私钥只能对应一个公钥,但是,可以通过某种确定性算法,先确定一个私钥k1,然后计算出k2、k3、k4……等其他私钥,就相当于只需要管理一个私钥,剩下的私钥可以按需计算出来。

这种根据某种确定性算法,只需要管理一个根私钥,即可实时计算所有“子私钥”的管理方式,称为HD钱包。

HD是Hierarchical Deterministic的缩写,意思是分层确定性。先确定根私钥root,然后根据索引计算每一层的子私钥:

root
│
├─────────────┬─────────────┐
│             │             │
▼             ▼             ▼
k0            k1            k2 ...
│             │             │
├──┬──┐       ├──┬──┐       ├──┬──┐
│  │  │       │  │  │       │  │  │
▼  ▼  ▼       ▼  ▼  ▼       ▼  ▼  ▼
k0 k1 k2 ...  k0 k1 k2 ...  k0 k1 k2 ...

对于任意一个私钥k,总是可以根据索引计算它的下一层私钥kn:

k_n=hdkey(k,\;n)k 
n
​    
 =hdkey(k,n)

即HD层级实际上是无限的,每一层索引从0~232,约43亿个子key。这种计算被称为衍生(Derivation)。

现在问题来了:如何根据某个私钥计算下一层的子私钥?即函数hdkey(k, n)如何实现?

HD钱包采用的计算子私钥的算法并不是一个简单的SHA-256,私钥也不是普通的256位ECDSA私钥,而是一个扩展的512位私钥,记作xprv,它通过SHA-512算法配合ECC计算出子扩展私钥,仍然是512位。通过扩展私钥可计算出用于签名的私钥以及公钥。

简单来说,只要给定一个根扩展私钥(随机512位整数),即可计算其任意索引的子扩展私钥。扩展私钥总是能计算出扩展公钥,记作xpub:

xprv ────────> xpub
  │              │
  │              │
  │              │
  ▼              ▼
xprv-0 ──────> xpub-0

从xprv及其对应的xpub可计算出真正用于签名的私钥和公钥。之所以要设计这种算法,是因为扩展公钥xpub也有一个特点,那就是可以直接计算其子层级的扩展公钥:

xpub
│
├───────┬───────┐
│       │       │
▼       ▼       ▼
xpub-0  xpub-1  xpub-2  ...

因为xpub只包含公钥,不包含私钥,因此,可以安全地把xpub交给第三方(例如,一个观察钱包),它可以根据xpub计算子层级的所有地址,然后在比特币的链上监控这些地址的余额,但因为没有私钥,所以只能看,不能花。

因此,HD钱包通过分层确定性算法,实现了以下功能:

  • 只要确定了扩展私钥xprv,即可根据索引计算下一层的任何扩展私钥;
  • 只要确定了扩展公钥xpub,即可根据索引计算下一层的任何扩展公钥;
  • 用户只需保存顶层的一个扩展私钥,即可计算出任意一层的任意索引的扩展私钥。

从理论上说,扩展私钥的层数是没有限制的,每一层的数量被限制在0~232,原因是扩展私钥中只有4字节作为索引,因此索引范围是0~232。

通常把根扩展私钥记作m,子扩展私钥按层级记作m/x/y/z等:

m
│
├──────────────────────┐
│                      │
▼                      ▼
m/0                    m/1 ...
│                      │
├─────┬─────┐          ├─────┬─────┐
│     │     │          │     │     │
▼     ▼     ▼          ▼     ▼     ▼
m/0/0 m/0/1 m/0/2 ...  m/1/0 m/1/1 m/1/2 ...

例如,m/0/2表示从m扩展到m/0(索引为0)再扩展到m/0/2(索引为2)。

安全性

HD钱包给私钥管理带来了非常大的方便,因为只需要管理一个根扩展私钥,就可以管理所有层级的所有衍生私钥。

但是HD钱包的扩展私钥算法有个潜在的安全性问题,就是如果某个层级的xprv泄露了,可反向推导出上层的xprv,继而推导出整个HD扩展私钥体系。为了避免某个子扩展私钥的泄漏导致上层扩展私钥被反向推导,HD钱包还有一种硬化的衍生计算方式(Hardened Derivation),它通过算法“切断”了母扩展私钥和子扩展私钥的反向推导。HD规范把索引0~231作为普通衍生索引,而索引231~232作为硬化衍生索引,硬化衍生索引通常记作0'、1'、2'……,即索引0'=231,1'=231+1,2'=231+2,以此类推。

因此,m/44'/0表示的子扩展私钥,它的第一层衍生索引44'是硬化衍生,实际索引是231+44=2147483692。从m/44'/0无法反向推导出m/44'。

在只有扩展公钥的情况下,只能计算出普通衍生的子公钥,无法计算出硬化衍生的子扩展公钥,即可计算出的子扩展公钥索引被限制在0~231。因此,观察钱包能使用的索引是0~231。

BIP-32

比特币的BIP-32规范详细定义了HD算法原理和各种推导规则,可阅读此文档以便实现HD钱包。

JSRUN闪电教程系统是国内最先开创的教程维护系统, 所有工程师都可以参与共同维护的闪电教程,让知识的积累变得统一完整、自成体系。 大家可以一起参与进共编,让零散的知识点帮助更多的人。
X
支付宝
9.99
无法付款,请点击这里
金额: 0
备注:
转账时请填写正确的金额和备注信息,到账由人工处理,可能需要较长时间
如有疑问请联系QQ:565830900
正在生成二维码, 此过程可能需要15秒钟