Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 10 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save Ankarrr/d82b49bbb481bf3181274cfd71b30bcc to your computer and use it in GitHub Desktop.
Save Ankarrr/d82b49bbb481bf3181274cfd71b30bcc to your computer and use it in GitHub Desktop.

【虛擬貨幣錢包】從 BIP32、BIP39、BIP44 到 Ethereum HD Wallet

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

虛擬貨幣錢包

錢包顧名思義是存放$$$。但在虛擬貨幣世界有點不一樣,我的帳戶資訊(像是我有多少錢)是儲存在區塊鏈上,實際存在錢包中的是我的帳戶對應的 key。有了這把 key 我就可以在虛擬貨幣世界證明我的身份、就可以更改我帳戶的狀態(像是送錢給別人)。這樣來說,虛擬貨幣錢包實際上是管理和儲存 key 的工具。這把 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 brow

  • 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 ethereumjs-util --save

初始化

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

產生 mnemonic code

var mnemonic = bip39.generateMnemonic()

取得的 mnemonic code 會像:

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

產生 HD wallet

先將 mnemonic code 轉成 binary 的 seed。

var seed = bip39.mnemonicToSeed(mnemonic)

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

var hdWallet = hdkey.fromMasterSeed(seed)

產生第一個 Ethereum Address

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

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

使用 keypair 中的公鑰產生 address。

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

Ethereum 很貼心,為了避免大家打錯 address(就把錢送錯人),Ethereum 讓 Address 變得比較難打?!�總之要用 EIP55: Mixed-case checksum address encoding 再進行編碼。

address1 = util.toChecksumAddress(address1.function toString() { [native code] }('hex'))

最後取得的 Address 會像:

0x685ce4CbDd5c19b64CA008cB85b83947e5318EFA

可以用 Mnemonic Code Converter 驗證結果

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


使用 Ethereum HD wallet

把 mnemonic code 記錄下來好好保存,就會是一個冷錢包(指不連網路的錢包,所以安全很多)。可以使用產生出來的 address 收 Ether 或任何 REC20 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

Chrome Extension Storage


References

其他相關 Ethereum JavaScript 套件

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment