区块链钱包-HD钱包

钱包发展

在比特币早期,中本聪就提出了每次交易使用一个地址这个概念,即假设你A地址上有100块,那么你向B地址转账20块之后,你剩下的80块会转入C地址,A地址彻底弃用,这样意味着A私钥也彻底弃用了。在早期的非确定性钱包,会一次性为用户生成100个私钥/地址对,使用完再继续生成,可想而知,这样管理资产非常麻烦。在这种背景下,有人提出了确定性钱包的概念,只要有一个种子,就能派生出海量的私钥/地址对,妥当保管好种子,就能完全掌控整个派生树,而HD钱包就是目前最常用的确定性钱包模型。
HD是Hierarchical-Deterministic(分层确定性)的缩写,这个概念是在BIP32中被首次提出。这里的分层可以理解成,一个大公司拥有一个种子,它可以为各个子部门派生子私钥,各个子部门又可以给子子部门派生子子私钥…这样做有很多好处:
○ 树状特征非常适用于有组织结构的公司使用
○ 每个父能看到自己派生的子的财务流向,而子不能看到父
○ 能够在不需知道私钥的前提下生成大量的公钥,非常适用于只负责收款的服务

Tips!

principle of avoiding address reuse: 提倡避免地址重复使用,当数字货币地址已经发生过一次转账就存在私钥泄漏的可能性。
BIP: Bitcoin Improvement Proposals(比特币改进提案),比特币是开源项目,所有人都可以给核心开发者提出建议。详见BIP项目地址:bips

种子与助记词

本质上,种子是一个数字,不方便记忆。
在BIP39中,提出了助记词方案来试图解决种子的存放问题。它的核心是通过随机生成 12 ~ 24 个容易记住便于抄录的单词,再由该单词序列通过 PBKDF2 与 HMAC-SHA512 函数创建出随机种子作为 BIP32 的种子。它有几个特性:
○ 规定熵的位数必须是 32 的整数倍,所以熵的长度取值位 128 到 256 之间取 32 的整数倍的值,分别为 128, 160, 192, 224, 256;
○ 校验和的长度为熵的长度/32 位, 所以校验和长度可为 4,5,6,7,8 位;
○ 助记词库有 2048 个词,用 11 位可全部定位词库中所有的词,作为词的索引,故一个词用 11 位表示,助记词的个数可为 (熵+校验和)/11,值为 12,15,18,21,24。
作为创建钱包的第一步,下面将以目前常用的128强度为例,简单阐述下生成助记词的过程:
1. 随机生成一个16字节的熵;
2. 对熵进行哈希计算,得到一个长度为32的数组;
3. 取熵的比特位,得到一个长度为128的数组;
4. 将熵hash前4比特作为校验位放于熵比特数组之后,得到一个长度为132的数组;
5. 以长度11切割第4步得到的数组,并转为10进制,得到12个数;
6. 以数字为序号,从助记词库取出对应的单词,得到助记词。
以下为一个示例实现: 在得到助记词之后,根据BIP39,我们将使用PBKDF2函数推算出根种子,其约定的参数如下:
1. 将助记词序列作为密码
2. “mnemonic” + passphrase 作为盐
3. 以 2048 为重复计算的次数
4. 以 HMAC-SHA512 为随机算法
5. 期望得到长度为512位(64字节)的密钥作为种子

主密钥与主链码

得到种子之后,我们需要通过种子推导出主密钥 (master key) 和主链码 (master chain code)。
根种子通过不可逆 HMAC-SHA512 算法推算出 512 位的哈希串,左 256 位是 Master Private key(m), 右 256 位是 master chain code, 通过 m 结合推导公钥的椭圆曲线算法能推导出与之对应的 264 位 master public Key (M)。chain code 作为推导下级密钥的熵。

Tips!

什么是扩展密钥?
密钥和链码的结合统称为扩展密钥 (Extended keys)。即256位的密钥和256位的链码串联起来的512位就是扩展密钥。
包含私钥的扩展密钥用以推导子私钥,从子私钥又可推导对应的公钥和比特币地址。
包含公钥的扩展密钥用以推导子公钥。
扩展密钥使用Base58Check算法加上特定的前缀编码,编码得到的包含私钥的前缀为xprv,包含公钥的扩展密钥前缀为xpub,相比比特币的公私钥,扩展密钥编码之后得到的长度为512或513位。

路径

当获得了主密钥和主链码之后,我们需要设计一套规则来推导子密钥和子链码以供交易使用。其中有个重要的概念就是路径。
路径是一套索引规范。现在我们假设整个HD钱包是一颗树,将主密钥视为树干,子私钥视为叶子,那么路径就可以视为叶子的坐标,可以准确的描述出叶子相对于树干的位置。
在比特币社区的BIP44协议中,提出了目前常用的5层路径建议,如下:
m/purpse’/coin_type’/account’/change/address_index
每一层都有特别的含义,分别是:
1. purpse:目前固定为44’(或者0x8000002C),表示遵守BIP44协议。该层使用硬化衍生。
2. coin_type:币种类型,使得一个主私钥可以管理多币种。其中比特币是0。该层使用硬化衍生。
3. account:账户,从0开始按顺序严格递增。它将密钥空间拆分为独立的账户身份,从而使得各个账户的资产永远不会搅在一起。该层使用硬化衍生。
4. change:是否为找零地址。常数0代表对外地址,常数1代表内部可见地址。在实际使用过程中,应当将对外地址用于收款,内部地址用于接收交易找零。该层使用正常衍生。
5. address_index:地址索引编号,从0开始按顺序严格递增。该层使用正常衍生。
例如:
○ 比特币6号账户22号找零地址:m/44’/0’/6’/1/22
○ 以太坊0号账户0号非找零地址:m/44’/60’/0’/0/0
最初,设计者出于安全考虑引入了硬化衍生。正常衍生的索引号范围为 [0x0, 0x7FFFFFFF],而硬化衍生的索引号范围为 [0x80000000, 0xFFFFFFFF];硬化衍生的索引号太长,一般为了便于阅读,都是会将索引号右上角加上撇号,譬如:0x80000000 记为 0’,0x80000001 记为 1’,以此类推。

Tips!

路径是允许不完整的。比如:如果公司总部仅仅推导到m/44’/0’/0,并将该层的密钥和链码给了财务部,那么就相当于财务部拥有了0号账户的衍生权。财务部就可以独立的用0/0、0/1、0/2…作为路径去接着推导总部HD钱包这个分支的子密钥。甚至可以将0号账户作为自己的主密钥,重新构建一个HD钱包。

增强扩展子密钥推导

至此,我们已经定义好了树干和坐标,可以准确定位到每个枝头,每片树叶了。根据BIP32的HKD(hardened key derivation formula)函数,结合父私钥、父链码和路径,推导出增强子私钥和增强子链码。

总结