private key

聊一聊助记词跟私钥

前言

一般在接触加密货币的时候,基本上第一步都是先安装个钱包工具(各式各样),然后在过程中会接触到助记词(mnemonic phrases),私钥(private key),钱包地址(address)等概念,在理解这几个原理之前,再加上类似于metamask这样的工具对这几个概念的区分并不太清晰,导致理解起来非常困难。

困惑之路

就拿最流行的MetaMask来说吧, 安装好就开始导入或者创建账户了。这时候MetaMask只能对应一个助记词,所以把他理解成只有一个钱包应该没问题。所以MetaMask:钱包:助记词=1:1:1的概念,但是MetaMask又可以创建多个账户,那么也就是助记词:账户=1:N。但是当我们创建多个账户的时候,助记词早就有了,那么一个助记词是怎么恢复那么多账户的呢?

解释

上面的这个问题困扰了我好久,主要原因还是不懂钱包原理的缘故。
其实助记词只是生成钱包的种子,一个助记词可以生成无数个钱包。所谓从助记词来“恢复”钱包,这种说法是有歧义的。
助记词是“生成”钱包,而不是“恢复”钱包,只不过按照规则,生成的钱包都是一致的。
如果说你在一个钱包工具里生成很多个账户,当你再其它地方再次导入时,钱包工具其实并不知道你原来的钱包工具里有多少个账户(有些工具会根据查询钱包地址是否有货币来帮你生成),你需要手动再次生成。

代码

用ethers.js来解释这个现象会比较容易理解以下。

钱包创建

1
2
3
4
5
6
7
8
9
10
11
12
import { ethers, utils } from "ethers";

const wallet = ethers.Wallet.createRandom();

const address = wallet.address;
console.log("address:", address);
const privateKey = wallet.privateKey;
console.log("privateKey:", privateKey);
const publicKey = wallet.publicKey;
console.log("publicKey:", publicKey);
const mnemonic = wallet.mnemonic;
console.log("mnemonic:", mnemonic);

默认钱包生成

1
2
3
4
5
const wallet2 = ethers.Wallet.fromMnemonic("museum...."); //默认path为"m/44'/60'/0'/0/0",也就是这个助记词的第一个钱包
console.log("wallet2.address:", wallet2.address);
console.log("wallet2.privateKey:", wallet2.privateKey);
console.log("wallet2.publicKey:", wallet2.publicKey);
console.log("wallet2.mnemonic:", wallet2.mnemonic);

第二个钱包以及之后的生成

1
2
3
4
5
const wallet3 = ethers.Wallet.fromMnemonic("museum....", "m/44'/60'/0'/0/1"); //第二个钱包的path为"m/44'/60'/0'/0/1"
console.log("wallet3.address:", wallet3.address);
console.log("wallet3.privateKey:", wallet3.privateKey);
console.log("wallet3.publicKey:", wallet3.publicKey);
console.log("wallet3.mnemonic:", wallet3.mnemonic);

也可以直接用HDNode来生成

1
2
3
4
5
6
7
const hdNode = utils.HDNode.fromMnemonic('museum....');
const firstAccount = hdNode.derivePath(`m/44'/60'/0'/0/0`); //第一个钱包
console.log("firstAccount:", firstAccount);
const secondAccount = hdNode.derivePath(`m/44'/60'/0'/0/1`);//第二个钱包
console.log("secondAccount:", secondAccount);
const thirdAccount = hdNode.derivePath(`m/44'/60'/0'/0/2`);//第三个钱包
console.log("thirdAccount:", thirdAccount);

总结

从上面可以看出其实钱包:账户是1:1的关系,而助记词:钱包是1:N的关系,虽然Metamask只能导入一个助记词,但是可以通过其它钱包密钥的形式,导入其它助记词生成的钱包。这个地方比较容易让人混淆。