1. 程式人生 > >Ethereum HD Wallet(虛擬貨幣錢包)-BIP32、BIP39、BIP44

Ethereum HD Wallet(虛擬貨幣錢包)-BIP32、BIP39、BIP44

參考https://cloud.tencent.com/info/78dbcfc6df58fb58b13b152ae154a3e0.html

BIP 全名是 Bitcoin Improvement Proposals,是提出 Bitcoin 的新功能或改進措施的檔案。

私鑰通過橢圓曲線生成公鑰, 公鑰通過雜湊函式生成地址,這兩個過程都是單向的。
實際上,數字錢包實際是一個管理私鑰(生成、儲存、簽名)的工具,注意錢包並不儲存資產,資產是在鏈上的。

如何建立賬號

建立賬號關鍵是生成一個私鑰, 私鑰是一個32個位元組的數, 生成一個私鑰在本質上在1到2^256之間選一個數字。
因此生成金鑰的第一步也是最重要的一步,是要找到足夠安全的熵源,即隨機性來源,只要選取的結果是不可預測或不可重複的,那麼選取數字的具體方法並不重要。
比如可以擲硬幣256次,用紙和筆記錄正反面並轉換為0和1,隨機得到的256位二進位制數字可作為錢包的私鑰。
從程式設計的角度來看,一般是通過在一個密碼學安全的隨機源(不建議大家自己去寫一個隨機數)中取出一長串隨機位元組,對其使用SHA256雜湊演算法進行運算,這樣就可以方便地產生一個256位的數字。

實際過程需要比較下是否小於n-1(n = 1.158 * 10^77, 略小於2^256),我們就有了一個合適的私鑰。否則,我們就用另一個隨機數再重複一次。這樣得到的私鑰就可以根據上面的方法進一步生成公鑰及地址。

 

1.bip32-協議與實現看bip32

錢包也是一個私鑰的容器,按照上面的方法,我們可以生成一堆私鑰(一個人也有很多賬號的需求,可以更好保護隱私),而每個私鑰都需要備份就特別麻煩的。
最早期的比特幣錢包就是就是這樣,還有一個暱稱:“Just a Bunch Of Keys(一堆私鑰)“

為了解決這種麻煩,就有了BIP32 提議

根據一個隨機數種子通過分層確定性推導的方式得到n個私鑰,這樣儲存的時候,只需要儲存一個種子就可以,私鑰可以推匯出來

BIP32提案的名字是:Hierarchical Deterministic Wallets, 就是我們所說的HD錢包。


來分析下這個分層推導的過程,第一步推導主祕鑰的過程:

根種子輸入到HMAC-SHA512演算法中就可以得到一個可用來創造主私鑰(m) 和 一個主鏈編碼( a master chain code)

這一步生成的祕鑰(由私鑰或公鑰)及主鏈編碼再加上一個索引號,將作為HMAC-SHA512演算法的輸入繼續衍生出下一層的私鑰及鏈編碼,如下圖:
衍生推導的方案其實有兩個:

  (1)一個用父私鑰推導(稱為強化衍生方程)

  (2)一個用父公鑰推導

同時為了區分這兩種不同的衍生,在索引號也進行了區分,索引號小於2^31用於常規衍生,而2^31到2^32-1之間用於強化衍生,為了方便表示索引號i',表示2^31+i。
因此增加索引(水平擴充套件及 通過子祕鑰向下一層(深度擴充套件)可以無限生成私鑰。
注意, 這個推導過程是確定(相同的輸入,總是有相同的輸出)也是單向的

,子金鑰不能推匯出同層級的兄弟金鑰,因為子金鑰並不知道 parent chain code,也不能推出父金鑰。如果沒有子鏈碼也不能推匯出孫金鑰。現在我們已經對分層推導有了認識。
一句話概括下BIP32就是:為了避免管理一堆私鑰的麻煩提出的分層推導方案

 

(2)用父公鑰推導

  需要三個引數:parent public key、parent chain code和index(要產生的是下一層的第幾個child),然後就能夠生成子私鑰和子chain code,根據子私鑰就能夠得到子公鑰,然後我們就能夠繼續使用子公鑰、子chain code和index再生成下一層的key,可以不停地推導下去。

 

相應協議網址-https://github.com/bitcoin/bips

2.bip39-協議和例項看bip39

線上轉換器-https://iancoleman.io/bip39/#english

 

3.bip44-協議和例項看bip44

bip32 就是沒有任何限制的協議,而 bip44 就是限制使用者,要求使用者從第四層定義開始取值,而前三層定義用於確定協議與幣種。

線上實現器-https://iancoleman.io/bip39/#english

 

4.幾個互相之間的關係:

生成mnemonic > 生成seed > 生成 Extended Public Key

生成地址主要依賴Extended Public Key,加上addressIndex(0至232-1)就可以確定一個地址.

BTC使用m/44’/0’/0’/0的 Extended Public Key 生成 m/44’/0’/0’/0/*,
ETH使用m/44’/60’/0’/0的 Extended Public Key 生成 m/44’/60’/0’/0/*,
mainnet的Extended Public Key以xpub做字首,例如:

xpub68WavebvyHHRwCR5ZaXviVuAU6AgmyYQabjq4giBBLcBB68MM5knf8aBh584hYmB18yYzkvmrH2pnXmUYdjgborGr3DrgH6zpkcDetpzuNB

擴充套件公鑰不等同於公鑰,擴充套件公鑰主要包含了3個資訊: 
1) 區塊鏈網路(mainnet 或 testnet) 
2) 公鑰 
3) chain code


實現:(參考https://www.jianshu.com/p/54a2b14dfdf2)

  • bip39:實作 BIP39,隨機產生新的 mnemonic code,並可以將其轉成 binary 的 seed。
  • ethereumjs-wallet:產生和管理公私鑰,我使用其中的 hdkey 子套件來建立 HD Wallet。
  • ethereumjs-util:集合許多 Ethereum 需要的運算功能。
npm install bip39 ethereumjs-wallet ethereumjs-util --save

 

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

//產生 mnemonic code
var mnemonic = bip39.generateMnemonic()
console.log(mnemonic)//ship dove behave merit will live other rough island curious desk push

//將 mnemonic code 轉成 binary 的 seed
var seed = bip39.mnemonicToSeed(mnemonic)
console.log(seed)
//<Buffer be 06 2a 79 97 41 32 c5 30 07 2b 11 00 b8 21 4c 56 84 fe a8 d1 28 f8 6f f2 58 4e 26 20 b6 f5 70 61 8b b9 a0 46 0a 94 5b ea d9 1e 8b c4 ee 4c 65 77 29 ... 14 more bytes>

//使用 seed 產生 HD Wallet
var hdWallet = hdkey.fromMasterSeed(seed)
console.log(hdWallet)
// EthereumHDKey {
//   _hdkey:
//    HDKey {
//      versions: { private: 76066276, public: 76067358 },
//      depth: 0,
//      index: 0,
//      _privateKey:
//       <Buffer 2f 29 d8 96 63 c4 8b 34 34 f1 6b 56 44 69 f3 26 f2 e4 7d 36 71 5c 8b 03 a9 ed 2d 5b 9a 31 27 8d>,
//      _publicKey:
//       <Buffer 03 56 04 61 7d f7 68 d3 16 13 8e cc 97 21 ff 56 73 ff dd ce 69 50 af 74 76 9b 73 41 12 ea 79 82 61>,
//      chainCode:
//       <Buffer 5c c6 6e 68 6c 81 b7 a9 11 43 62 de 7b 50 a5 b8 06 66 73 a6 6f 87 4f cd ce 76 a5 95 e2 d1 0d 3e>,
//      _fingerprint: 503483031,
//      parentFingerprint: 0,
//      _identifier:
//       <Buffer 1e 02 8a 97 8b cf 89 e5 1b 0e b4 ad 06 e2 7c 26 dc b3 9f a8> } }

//產生 Wallet 中第一個帳戶的第一組 keypair,以太幣
var key1 = hdWallet.derivePath("m/44'/60'/0'/0/0")
console.log(key1)
// EthereumHDKey {
//   _hdkey:
//    HDKey {
//      versions: { private: 76066276, public: 76067358 },
//      depth: 5,
//      index: 0,
//      _privateKey:
//       <Buffer 97 6d ad 55 91 fa f4 69 4d 36 27 c4 ab 4a 2d b4 02 32 d0 39 4e 4f da 55 06 4f 69 12 8c 67 85 b9>,
//      _publicKey:
//       <Buffer 02 6f a7 5a 3f 3b 10 9a ce 14 4c 35 91 a3 22 6e 48 b0 25 bc ad ee d8 5e de e6 ed a8 40 92 b7 a8 1f>,
//      chainCode:
//       <Buffer b8 e1 17 60 eb 3e 4c 58 44 3f bb e4 cd 9c 6f 0d 55 ed 84 8b 60 6b 64 42 e2 3f 79 fd 33 29 0e 8b>,
//      _fingerprint: 1477067077,
//      parentFingerprint: 3604950195,
//      _identifier:
//       <Buffer 58 0a 41 45 06 d2 7e ef c8 a8 fe 33 8b ca 75 04 9f 9d e2 b5> } }




//用 keypair 中的公鑰產生 address
var address1 = util.pubToAddress(key1._hdkey._publicKey, true)
console.log(address1)//<Buffer 29 f6 f9 fb d3 fe 8c dd 39 83 57 1a b6 33 8c bb 1c b4 7a e2>

//生成 checksum address 
address1 = util.toChecksumAddress(address1.toString('hex'))
console.log(address1)//0x29F6F9fbd3Fe8cDd3983571AB6338CBB1CB47ae2