Wallet

作为用户,Wallet 是区块链的入口;现阶段主流的 Wallet 类型有:HD Wallet,MultiSig wallet,Custodial wallet,Hardware wallet,Paper wallet,MPC Wallet 等。作为开发者,HD Wallet、MultiSig Wallet、MPC Wallet 是需要重点掌握的。

HD wallet

钱包是用来存钱的,在区块链中,我们的数字资产都会对应到一个账户地址上, 只有拥有账户的钥匙(私钥)才可以对资产进行消费(用私钥对消费交易签名)。

他们之间的关系如下:

wallet wallet

私钥通过椭圆曲线生成公钥, 公钥通过哈希函数生成地址,这两个过程都是单向的; 所以,数字钱包实际是一个管理私钥(生成、存储、签名)的工具,注意钱包并不保存资产,资产是在链上的。

生成私钥的本质是在 1 到 2^256 之间选一个数字:生成密钥的第一步也是最重要的一步,是要找到足够安全的熵源,即随机性来源,只要选取的结果是不可预测或不可重复的

HD, Hierarchical Deterministic, 叫做分层确定性钱包,是一种可以从单个种子(seed)衍生出一系列私钥的钱包结构,这些私钥都可以被根种子所控制,这样就可以用一个种子来管理多个账户。

在 Mastering Bitcoin 中,有一章是来介绍 Wallet 的,有必要看一下:Mastering Bitcoin - Wallet, 中文版看这里

HD 钱包允许我们使用一个密钥管理很多个衍生私钥,进而控制多个地址,bip32 规范中使用一种树形结构来进行管理。

HD Bip32 HD Bip32

关于 HD wallet 有一系列规范:

bip39

bip39 规范主要关于生成确定性密钥的助记码,使用一组预定义的单词来表示一串随机数,这样就可以方便的记忆和传输。主要包含两部分:生成助记码和从主机码转成二进制私钥种子。

我们来看看 bip39 的技术细节

如何生成助机词

  1. Create a random sequence (entropy) of 128 to 256 bits.
  2. Create a checksum of the random sequence by taking the first (entropy-length/32) bits of its SHA256 hash.
  3. Add the checksum to the end of the random sequence.
  4. Split the result into 11-bit length segments.
  5. Map each 11-bit value to a word from the predefined dictionary of 2048 words.
  6. The mnemonic code is the sequence of words.

随机熵和单词长度的关系:

Entropy (bits)Checksum (bits)Entropy + checksum (bits)Mnemonic length (words)
128413212
160516515
192619818
224723121
256826424

用助记词生成种子

  1. The first parameter to the PBKDF2 key-stretching function is the mnemonic produced from step 6.
  2. 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.
  3. 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

参考实现

Keys 和 Addresses

在上面介绍的几个 bip 规范中,其实少了一些细节,比如私钥如何生成公钥?公钥如何生成地址?不同的链使用的算法都一样吗?…

关于这些问题可以参考这篇文章:Mastering Bitcoin - Keys and Addresses

ChainPrivate Key -> Public KeyCompressed Public Key ?Public Key -> AddressAddress Format
BTCECDSA (ecc Secp256k1)YesSHA256 + RIPEMD160Base58
ETHECDSA (ecc Secp256k1)YesKeccak256Hex
BNBECDSA (ecc Secp256k1)YesKeccak256Bech32

对于地址生成算法,不同的链不太一样

  • 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