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. 代幣轉賬