Home | 简体中文 | 繁体中文 | 杂文 | 知乎专栏 | Github | OSChina 博客 | 云社区 | 云栖社区 | Facebook | Linkedin | 视频教程 | 打赏(Donations) | About
知乎专栏多维度架构 | 微信号 netkiller-ebook | QQ群:128659835 请注明“读者”

16.14. HD Wallet(Hierarchical Deterministic wallet)

BIP32 定义 Hierarchical Deterministic wallet (简称 "HD Wallet"),是一个系统可以从单个seed产生树状结构储存多组 keypairs(私钥和公钥)

BIP39 定义钱包助记词和seed生成规则,一般由 12 -24个单字组成,称为 mnemonic。助记词列表,https://github.com/bitcoin/bips/blob/master/bip-0039/english.txt

BIP44 基于 BIP32 的系统,赋予树状结构中的各层特殊的意义。让同一个 seed 可以支援多币种、多帐户等 (btc一般是 m/44'/0'/0’/0,eth一般是 m/44'/60'/0'/0)

		
npm install bip39 ethereumjs-wallet ethereumjs-util --save
		
		

16.14.1. 创建项目

导入开发包

			
var bip39 = require('bip39')
var hdkey = require('ethereumjs-wallet/hdkey')
var util = require('ethereumjs-util')
			
			

生成 mnemonic code

			
var mnemonic = bip39.generateMnemonic()
			
			

生成 HD Wallet 首先将 mnemonic code 转成 binary二进制的 seed

			
var seed = bip39.mnemonicToSeed(mnemonic)
			
			

生成 Master Key 地址 "m/44'/60'/0'/0/0" 使用 seed 生成 HD Wallet。

			
var hdwallet = hdkey.fromMasterSeed(seed)
			
			

从路径 m/44'/60'/0'/0/0 导入 Master Key 并生成 Wallet 中第一个帐户的第一组 keypair。

			
var key1 = hdwallet.derivePath("m/44'/60'/0'/0/0")
			
			

使用 keypair 中的公钥产生 address。

			
var address1 = util.pubToAddress(key1._hdkey._publicKey, true)
			
			

获得以太坊钱包地址

			
address1 = util.toChecksumAddress(address1.toString('hex'))
			
			

操作演示

			
[ethereum@netkiller web3.js]$ node
> var bip39 = require('bip39')
undefined
> var hdkey = require('ethereumjs-wallet/hdkey')
undefined
> var util = require('ethereumjs-util')
undefined
> var mnemonic = bip39.generateMnemonic()
undefined
> mnemonic
'client dune unfair assume level width bind control mad member old crystal'
> var seed = bip39.mnemonicToSeed(mnemonic)
undefined
> seed
<Buffer 51 12 a3 47 f3 bb b9 24 80 ac 05 6c ce 8c 9f dd b2 98 c8 d3 06 8f 4d 0b 6c 90 86 aa d4 b6 41 36 35 5f b4 42 89 b5 e4 6d 43 9b cf 8d 6a d7 9b 45 3e 5a ... >
> var hdwallet = hdkey.fromMasterSeed(seed)
undefined
> hdwallet
EthereumHDKey {
  _hdkey:
   HDKey {
     versions: { private: 76066276, public: 76067358 },
     depth: 0,
     index: 0,
     _privateKey: <Buffer 1c 37 00 1b f7 1d a5 de 3a 8a 4c e8 54 2d 69 78 81 f3 aa a9 d5 3e 64 74 bd ea 76 28 44 07 d3 04>,
     _publicKey: <Buffer 03 77 fc 6b c7 f3 e3 51 01 db 95 0a a9 0f c0 7f 31 40 af 75 f8 7a 4f 5a c3 4c 93 ac cb 44 a3 20 5f>,
     chainCode: <Buffer 51 fe 32 23 a0 ab aa 10 5d ff 90 28 26 dc fc 86 fc 5f 8c dc 1b b7 39 31 7e 2d b8 a4 77 33 45 3a>,
     _fingerprint: 1056395940,
     parentFingerprint: 0,
     _identifier: <Buffer 3e f7 52 a4 ed 86 00 f7 ac 4d 1a b4 15 1c 0d 87 cd 7d fe de> } }
> var key1 = hdwallet.derivePath("m/44'/60'/0'/0/0")
undefined
> key1
EthereumHDKey {
  _hdkey:
   HDKey {
     versions: { private: 76066276, public: 76067358 },
     depth: 5,
     index: 0,
     _privateKey: <Buffer f5 92 b7 bf 06 ca 9f d7 69 6b a9 5d 6e d8 e3 57 de 6a 23 79 b6 d5 fe 1f fd 53 c6 b4 b0 63 cd 4a>,
     _publicKey: <Buffer 02 99 ff bb ea 3d 80 e1 8c d5 54 a1 6e 6a ca b2 4b 7e 69 3d 1d 2d 8a 68 f8 61 bf 18 dc 4a f8 d0 26>,
     chainCode: <Buffer 5a 9b b2 0e 7a 62 07 b0 82 db e5 5a 1f 17 4b 47 8a 64 cf 40 67 d5 49 09 89 da aa 33 66 00 d7 e6>,
     _fingerprint: 3510386860,
     parentFingerprint: 1205114865,
     _identifier: <Buffer d1 3c 40 ac 09 92 fc d7 a4 14 8e d8 91 d1 a7 21 55 7e b8 e3> } }
> var address1 = util.pubToAddress(key1._hdkey._publicKey, true)
undefined
> address1
<Buffer 37 2f da 02 e8 a1 ec a5 13 f2 ee 59 01 dc 55 b8 b5 dd 74 11>
> address1.toString('hex')
'372fda02e8a1eca513f2ee5901dc55b8b5dd7411'
			
			

16.14.2. 生成第二个钱包

只需递增最后一位数即可 "m/44'/60'/0'/0/1", "m/44'/60'/0'/0/2","m/44'/60'/0'/0/3"

			
var key2 = hdwallet.derivePath("m/44'/60'/0'/0/1")
			
			

使用 keypair 中的公钥产生 address。

			
var address2 = util.pubToAddress(key2._hdkey._publicKey, true)
			
			

获得以太坊钱包地址

			
address2 = util.toChecksumAddress(address2.toString('hex'))
			
			

16.14.3. Mnemonic Code Converter

https://iancoleman.io/bip39/

输入 mnemonic,选择 ETH - Ethereum

系统将计算出钱包地址

16.14.4. HD Wallet 例子

			
var bip39 = require('bip39');
var hdkey = require('ethereumjs-wallet/hdkey');
var util = require('ethereumjs-util');

mnemonic = 'client dune unfair assume level width bind control mad member old crystal';
var seed = bip39.mnemonicToSeed(mnemonic);
var hdwallet = hdkey.fromMasterSeed(seed);
var key1 = hdwallet.derivePath("m/44'/60'/0'/0/0");
var address1 = util.pubToAddress(key1._hdkey._publicKey, true);
var address = util.toChecksumAddress(address1.toString('hex'));

coinbase = "0xaa96686a050e4916afbe9f6d8c5107062fa646dd";
contractAddress = "0x9ABcF16f6685fE1F79168534b1D30056c90B8A8A"

console.log(address);

fs = require('fs');
const Web3 = require('web3');
const HDWalletProvider = require("truffle-hdwallet-provider");

const web3 = new Web3(new HDWalletProvider(mnemonic,'http://localhost:8545'));
console.log(web3.version)

web3.eth.getBalance("0xaa96686a050e4916afbe9f6d8c5107062fa646dd").then(function(balance){
      console.log( web3.utils.fromWei(balance) );
});

web3.eth.getBalance(address).then(function(balance){
      console.log( web3.utils.fromWei(balance) );
});

const abi = fs.readFileSync('output/NetkillerToken.abi', 'utf-8');
const contract = new web3.eth.Contract(JSON.parse(abi), contractAddress, { from: address, gas: 100000});

contract.methods.balanceOf(coinbase).call().then(console.log).catch(console.log);
contract.methods.balanceOf(address).call().then(console.log).catch(console.log);
			
			

16.14.5. 获得钱包地址和私钥

			
const bip39 = require('bip39');
const hdkey = require('ethereumjs-wallet/hdkey');

const mnemonic = 'client dune unfair assume level width bind control mad member old crystal';
const hdwallet = hdkey.fromMasterSeed(bip39.mnemonicToSeed(mnemonic));
const path = "m/44'/60'/0'/0/0";
const wallet = hdwallet.derivePath(path).getWallet();

const address = `0x${wallet.getAddress().toString('hex')}`;
const privateKey = wallet.getPrivateKey().toString('hex');
// wallet._privKey.toString('hex');
console.log(`Address: ${address}`);
console.log(`Private Key: ${privateKey}`);

			
			

16.14.6. truffle.js 例子

			
npm install truffle-hdwallet-provider
			
			
			

var HDWalletProvider = require("truffle-hdwallet-provider");

var mnemonic = "opinion destroy betray ...";

module.exports = {
  networks: {
    development: {
      host: "localhost",
      port: 8545,
      network_id: "*" // Match any network id
    },
    ropsten: {
      provider: new HDWalletProvider(mnemonic, "https://ropsten.infura.io/"),
      network_id: 3
    }
  }
};
			
			

16.14.7. Mnemonic To Seed 加密

没有加密 seed 是很不安全的,任何人都能通过 Mnemonic 还原出公钥和私钥。

			
var bip39 = require('bip39')
var hdkey = require('ethereumjs-wallet/hdkey')
var util = require('ethereumjs-util')
var mnemonic = bip39.generateMnemonic()

var password = "http://www.netkiller.cn"
var seed = bip39.mnemonicToSeed(mnemonic, password)
var hdwallet = hdkey.fromMasterSeed(seed)
var key1 = hdwallet.derivePath("m/44'/60'/0'/0/0")
var address1 = util.pubToAddress(key1._hdkey._publicKey, true)
address1 = util.toChecksumAddress(address1.toString('hex'))
			
			

16.14.8. 中文助记词

			
var bip39 = require('bip39')
var mnemonic = bip39.generateMnemonic(128, null, bip39.wordlists.chinese_simplified)
console.log(mnemonic)

var seed = bip39.mnemonicToSeed(mnemonic)
var hdwallet = hdkey.fromMasterSeed(seed)
var key1 = hdwallet.derivePath("m/44'/60'/0'/0/0")
var address1 = util.pubToAddress(key1._hdkey._publicKey, true)
address1 = util.toChecksumAddress(address1.toString('hex'))
			
			

演示

			
> var bip39 = require('bip39')
undefined

> var mnemonic = bip39.generateMnemonic(128, bip39.randomBytes, bip39.wordlists.chinese_simplified)
undefined

> console.log(mnemonic)
俄 判 菌 诗 仪 偏 方 都 激 输 失 稀
undefined
			
			

16.14.9. 代币转账