【加密貨幣錢包】從 BIP32、BIP39、BIP44 到 Ethereum HD Wallet

Anderson Chen
Taipei Ethereum Meetup
8 min readFeb 5, 2018

錢包是很多人第一次接觸 Ethereum 或其他加密貨幣的地方。不管是用手機或瀏覽器的錢包,相信很多人都對一串陌生的單字感到好奇(而且很重要還要備份)。這是源自於 Bitcoin 中錢包的設計,採用這套機制的錢包通常稱為 HD Wallet本篇希望簡述 HD Wallet 的架構,再使用 JavaScript 套件從頭創建一個 Ethereum HD Wallet

加密貨幣錢包

錢包顧名思義是存放 $$$。但在虛擬貨幣世界有點不一樣,我的帳戶資訊(像是我有多少錢)是儲存在區塊鏈上,實際存在錢包中的是我的帳戶對應的 key。有了這把 key 我就可以在虛擬貨幣世界證明我的身份、就可以更改我帳戶的狀態(像是送錢給別人)。這樣來說,虛擬貨幣錢包實際上是管理和儲存 key 的工具。這把 key 就是我的私鑰(Private Key),私鑰會有對應的公鑰(Public Key),再從公鑰衍生出我的帳戶。

Ledger 虛擬貨幣錢包

BIP32, BIP39, BIP44

BIP 全名是 Bitcoin Improvement Proposals,是提出 Bitcoin 的新功能或改進措施的文件。可由任何人提出,經過審核後公佈在 bitcoin/bips 上。BIP 和 Bitcoin 的關係,就像是 RFC 之於 Internet。

而其中的 BIP32, BIP39, BIP44 共同定義了目前被廣泛使用的 HD Wallet,包含其設計動機和理念、實作方式、實例等。

  • BIP32:定義 Hierarchical Deterministic wallet (簡稱 “HD Wallet”),是一個系統可以從單一個 seed 產生一樹狀結構儲存多組 keypairs(私鑰和公鑰)。好處是可以方便的備份、轉移到其他相容裝置(因為都只需要 seed),以及分層的權限控制等。
BIP32 定義的 HD Wallet
  • BIP39:將 seed 用方便記憶和書寫的單字表示。一般由 12 個單字組成,稱為 mnemonic code(phrase),中文稱為助記詞或助記碼。例如:
rose rocket invest real refuse margin festival danger anger border idle brown
  • BIP44:基於 BIP32 的系統,賦予樹狀結構中的各層特殊的意義。讓同一個 seed 可以支援多幣種、多帳戶等。各層定義如下:
m / purpose' / coin_type' / account' / change / address_index

其中的 purporse' 固定是 44',代表使用 BIP44。而 coin_type' 用來表示不同幣種,例如 Bitcoin 就是 0',Ethereum 是 60'

Ethereum HD Wallet

Ethereum 的錢包目前均採用以上 Bitcoin HD Wallet 的架構,並訂 coin_type'60',可以在 ethereum/EIPs/issues 中看到相關的討論。舉例來說,在一個 Ethereum HD Wallet 中,第一個帳戶(這裡的帳戶指 BIP44 中定義的 account')的第一組 keypair,其路徑會是 m/44'/60'/0'/0/0

創建 Ethereum HD Wallet

使用的 JavaScript 套件包含:

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

安裝套件

npm install bip39 ethereumjs-wallet --save

匯入套件

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

產生 mnemonic code

const mnemonic = bip39.generateMnemonic()

取得的 mnemonic code 會像:

rose rocket invest real refuse margin festival danger anger border idle brown

產生 HD wallet

先將 mnemonic code 轉成 binary 的 seed。

const seed = bip39.mnemonicToSeedSync(mnemonic)

使用 seed 產生 HD Wallet。如果要說更明確,就是產生 Master Key 並記錄起來。

const hdWallet = hdkey.fromMasterSeed(seed)

產生第一個 Ethereum Address

產生 Wallet 中第一個帳戶的第一組 keypair。可以從 Master Key,根據其路徑 m/44'/60'/0'/0/0 推導出來。

const keyPair1 = hdWallet.derivePath("m/44'/60'/0'/0/0")

使用該 keypair 產生 wallet 物件。

const wallet1 = keyPair1.getWallet()

取得該 wallet 的 address。

const address1 = wallet1.getAddressString()

取得的 Address:

0x685ce4cbdd5c19b64ca008cb85b83947e5318efa

Encoding Address

一般會用 EIP55: Mixed-case checksum address encoding 再進行編碼。許多錢包也支援用戶輸入沒經過編碼的 Address,那就會跳過 checksum 機制(底下留言有更深入討論),建議還是使用編碼過的 Address。

const address1 = wallet1.getChecksumAddressString()

最後取得的 Address 會像:

0x685ce4CbDd5c19b64CA008cB85b83947e5318EFA

這便是一個常見的 Ethereum Address。

可以用 Mnemonic Code Converter 驗證結果

輸入 mnemonic code
產生 Address、公鑰、私鑰,結果和我取得的 Address 一致

使用 Ethereum HD Wallet

把 mnemonic code 記錄下來好好保存,就會是一個冷錢包(指不連網路的錢包,所以安全很多)。可以使用產生出來的 Address 收 Ether 或任何 ERC20 Token。要送錢的話,可以匯入到任一個支援 Ethereum HD Wallet 的錢包。常用的 Ethereum HD wallet 有在瀏覽器使用的 MyEtherWallet、MetaMask 和在手機使用的 imToken 等。

MetaMask

題外話,MetaMask 如何在瀏覽器儲存我們的 mnemonic code?

相信大家都瞭解了,有 mnemonic code 就可以產生 HD Wallet 中所有的 keys。有了 keys 就可以任意送錢包中的 Ether 或 Token 給別人。所以 mnemonic code 很重要!!!那這麼重要的東西保存在瀏覽器不會很危險嗎?我便研究下我常用的 MetaMask 瀏覽器錢包。

MetaMask 將加密後的 mnemonic code 存在瀏覽器的 Local Storage(一塊只存在 Local 且不會過期的資料區塊)。加密使用用戶另外輸入的密碼,再匯入時會要求用戶設定密碼(如上圖),而每一次重新開啓錢包都會要求輸入密碼。解密算法有 Open Source,也有線上 Live Demo

MetaMask Local Storage

感謝 Jiyi 大大提供密碼學專業知識,雖然詳細的數學計算本篇沒有提到,但讓我有底氣的完成這篇文章。

--

--