Wallet
作为用户,Wallet 是区块链的入口;现阶段主流的 Wallet 类型有:HD Wallet,MultiSig wallet,Custodial wallet,Hardware wallet,Paper wallet,MPC Wallet 等。作为开发者,HD Wallet、MultiSig Wallet、MPC Wallet 是需要重点掌握的。
HD wallet
钱包是用来存钱的,在区块链中,我们的数字资产都会对应到一个账户地址上, 只有拥有账户的钥匙(私钥)才可以对资产进行消费(用私钥对消费交易签名)。
他们之间的关系如下:
私钥通过椭圆曲线生成公钥, 公钥通过哈希函数生成地址,这两个过程都是单向的; 所以,数字钱包实际是一个管理私钥(生成、存储、签名)的工具,注意钱包并不保存资产,资产是在链上的。
生成私钥的本质是在 1 到 2^256 之间选一个数字:生成密钥的第一步也是最重要的一步,是要找到足够安全的熵源,即随机性来源,只要选取的结果是不可预测或不可重复的
HD, Hierarchical Deterministic, 叫做分层确定性钱包,是一种可以从单个种子(seed)衍生出一系列私钥的钱包结构,这些私钥都可以被根种子所控制,这样就可以用一个种子来管理多个账户。
在 Mastering Bitcoin 中,有一章是来介绍 Wallet 的,有必要看一下:Mastering Bitcoin - Wallet, 中文版看这里
HD 钱包允许我们使用一个密钥管理很多个衍生私钥,进而控制多个地址,bip32 规范中使用一种树形结构来进行管理。
关于 HD wallet 有一系列规范:
bip39
bip39 规范主要关于生成确定性密钥的助记码,使用一组预定义的单词来表示一串随机数,这样就可以方便的记忆和传输。主要包含两部分:生成助记码和从主机码转成二进制私钥种子。
我们来看看 bip39 的技术细节
如何生成助机词
- Create a random sequence (entropy) of 128 to 256 bits.
- Create a checksum of the random sequence by taking the first (entropy-length/32) bits of its SHA256 hash.
- Add the checksum to the end of the random sequence.
- Split the result into 11-bit length segments.
- Map each 11-bit value to a word from the predefined dictionary of 2048 words.
- The mnemonic code is the sequence of words.
随机熵和单词长度的关系:
Entropy (bits) | Checksum (bits) | Entropy + checksum (bits) | Mnemonic length (words) |
---|---|---|---|
128 | 4 | 132 | 12 |
160 | 5 | 165 | 15 |
192 | 6 | 198 | 18 |
224 | 7 | 231 | 21 |
256 | 8 | 264 | 24 |
用助记词生成种子
- The first parameter to the PBKDF2 key-stretching function is the mnemonic produced from step 6.
- The second parameter to the PBKDF2 key-stretching function is a salt. The salt is composed of the string constant “mnemonic” concatenated with an optional user-supplied passphrase string.
- PBKDF2 stretches the mnemonic and salt parameters using 2048 rounds of hashing with the HMAC-SHA512 algorithm, producing a 512-bit value as its final output. That 512-bit value is the seed.
参考实现:
源码解析
func NewEntropy(bitSize int) ([]byte, error) {
// 验证位数在 128 到 256 之间,并且是 32 的倍数
if err := validateEntropyBitSize(bitSize); err != nil {
return nil, err
}
entropy := make([]byte, bitSize/8)
// 这里使用的是 crypto/rand 包,而不是 math/rand 包,因为 math/rand 包是伪随机数生成器
// 这里显然需要一个真随机数生成器
_, _ = rand.Read(entropy)
return entropy, nil
}
// 用法
entropy, _ := bip39.NewEntropy(256)
mnemonic, _ := bip39.NewMnemonic(entropy)
seed := bip39.NewSeed(mnemonic, "hello")
总结一下:bip39 的主要作用就是把随机生成的 seed 转成助记词,方便记忆、传输等,不管是导入、导出还是备份都非常方便,并且从助记词也可以还原出 seed。
bip32 & bip44
从种子生成钱包
上面了解了 bip39,通过 bip39 我们可以获取一个助记词序列,它本质上是一个随机数,这个随机数被用来当作 seed 来生成私钥。bip32 规范主要是定义了从 seed 生成私钥的过程,它定义了一种树形结构来管理私钥,这种树形结构的每个节点都有一个索引,这个索引可以用来生成子节点,这样就可以方便的管理多个私钥。
关于 extended key 也可以看这里:extended keys
参考实现
- go 版本的 hdkeychain - 里面包含对 extended key 的实现,也就是实现了 bip32 规范
Keys 和 Addresses
在上面介绍的几个 bip 规范中,其实少了一些细节,比如私钥如何生成公钥?公钥如何生成地址?不同的链使用的算法都一样吗?…
关于这些问题可以参考这篇文章:Mastering Bitcoin - Keys and Addresses
Chain | Private Key -> Public Key | Compressed Public Key ? | Public Key -> Address | Address Format |
---|---|---|---|---|
BTC | ECDSA (ecc Secp256k1) | Yes | SHA256 + RIPEMD160 | Base58 |
ETH | ECDSA (ecc Secp256k1) | Yes | Keccak256 | Hex |
BNB | ECDSA (ecc Secp256k1) | Yes | Keccak256 | Bech32 |
对于地址生成算法,不同的链不太一样
- Bitcoin 采用 SHA256 + RIPEMD160, 然后再进行 Base58 编码
- Ethereum 采用 Keccak256, 然后再进行 Hex 编码
- 对公钥做 Keccak-256 哈希运算,然后取最后的 40 位 16 进制字符
实现一个支持多链的 HD 本地钱包
一些想法
- 这个本地钱包,我们暂时叫他 hdkms,后面都这么叫
- hdkms 支持管理多个钱包,可以通过指定 name 来区分不同的 hd wallet
- wallet1 -> mnemonic -> seed -> master private key -> local keystore file1
- wallet2 -> mnemonic -> seed -> master private key -> local keystore file2
- hdkms 支持导入助记词、私钥
- hdkms 支持导出助记词、私钥
- hdkms 支持生成地址,并且可以指定 index
- hdkms 可以查看 address 的余额
- hdkms 支持多链
- 比如 Bitcoin, Ethereum, Binance Smart Chain etc.
- hdkms 可以离线签署交易,生成签名后的交易数据
- 支持 native token 的 transfer
- 支持 erc20
- 支持 NFT
- 支持任意类型的合约
- hdkms 可以在线广播交易
- broadcast tx
- 查询 tx 状态
- hdkms 一些高级功能
- 支持 event 监听
- 支持动态手续费,比如 gas fee,network fee 等
- …
# 考虑怎么使用
# 1. 提供命令行工具,比如 hd-kms
# 创建钱包
hdkms wallet generate --wallet-name test1 --passphase 123456
hdkms wallet import --wallet-name test1 --mnemonic "" --passphase 123456
$ hdkms wallet export --wallet-name test1
$ {"mnemonic": "xxx", "passphase": ""}
TODOs
- 搭建起项目的基本框架
- 搞定 cli 及其扩展子命令
- 支持多个子命令的调用,比如 hdkms wallet generate, hdkms wallet list …
- 支持 Ethereum
- generate wallet
- mnemonic
- store mnemonic or private key to local keystore file
- get address by index
- query balance by address