錢包是很多人第一次接觸 Ethereum 或其他虛擬貨幣的地方。不管是用手機(jī)或瀏覽器的錢包允扇,相信很多人都對一串陌生的單字感到好奇(而且很重要還要備份)缠局。這是源自于 Bitcoin 中錢包的設(shè)計,采用這套機(jī)制的錢包通常稱為 HD Wallet考润。本篇希望簡述 HD Wallet 的架構(gòu)狭园,再使用 JavaScript 套件從頭創(chuàng)建一個 Ethereum HD Wallet。
虛擬貨幣錢包
錢包顧名思義是存放$$$糊治。但在虛擬貨幣世界有點(diǎn)不一樣唱矛,我的帳戶資訊(像是我有多少錢)是儲存在區(qū)塊鏈上,實際存在錢包中的是我的帳戶對應(yīng)的 key井辜。有了這把 key 我就可以在虛擬貨幣世界證明我的身份绎谦、就可以更改我?guī)舻臓顟B(tài)(像是送錢給別人)。這樣來說粥脚,虛擬貨幣錢包實際上是管理和儲存 key 的工具窃肠。這把 key 就是我的私鑰,而帳戶是從我的公鑰衍伸出來阿逃。
BIP32, BIP39, BIP44
BIP 全名是 Bitcoin Improvement Proposals铭拧,是提出 Bitcoin 的新功能或改進(jìn)措施的文件∈扬保可由任何人提出搀菩,經(jīng)過審核后公布在 bitcoin/bips 上。BIP 和 Bitcoin 的關(guān)系破托,就像是 RFC 之于 Internet肪跋。
而其中的 BIP32, BIP39, BIP44 共同定義了目前被廣泛使用的 HD Wallet不傅,包含其設(shè)計動機(jī)和理念仪际、實作方式、實例等羔巢。
- BIP32:定義 Hierarchical Deterministic wallet (簡稱 "HD Wallet")萝映,是一個系統(tǒng)可以從單一個 seed 產(chǎn)生一樹狀結(jié)構(gòu)儲存多組 keypairs(私鑰和公鑰)吴叶。好處是可以方便的備份、轉(zhuǎn)移到其他相容裝置(因為都只需要 seed)序臂,以及分層的權(quán)限控制等蚌卤。
-
BIP39:將 seed 用方便記憶和書寫的單字表示实束。一般由 12 個單字組成,稱為 mnemonic code(phrase)逊彭,中文稱為助記詞或助記碼咸灿。例如:
rose rocket invest real refuse margin festival danger anger border idle brown
-
BIP44:基于 BIP32 的系統(tǒng),賦予樹狀結(jié)構(gòu)中的各層特殊的意義侮叮。讓同一個 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 的架構(gòu),并訂 coin_type'
為 60'
孔庭,可以在 ethereum/EIPs/issues 中看到相關(guān)的討論尺上。舉例來說,在一個 Ethereum HD Wallet 中圆到,第一個帳戶(這里的帳戶指 BIP44 中定義的 account'
)的第一組 keypair怎抛,其路徑會是 m/44'/60'/0'/0/0
。
創(chuàng)建 Ethereum HD wallet
使用的 JavaScript 套件包含:
- bip39:實作 BIP39芽淡,隨機(jī)產(chǎn)生新的 mnemonic code马绝,并可以將其轉(zhuǎn)成 binary 的 seed。
- ethereumjs-wallet:產(chǎn)生和管理公私鑰挣菲,我使用其中的 hdkey 子套件來創(chuàng)建 HD Wallet富稻。
- ethereumjs-util:集合許多 Ethereum 需要的運(yùn)算功能。
安裝套件
npm install bip39 ethereumjs-wallet ethereumjs-util --save
匯入套件
var bip39 = require('bip39')
var hdkey = require('ethereumjs-wallet/hdkey')
var util = require('ethereumjs-util')
產(chǎn)生 mnemonic code
var mnemonic = bip39.generateMnemonic()
取得的 mnemonic code 會像:
rose rocket invest real refuse margin festival danger anger border idle brown
產(chǎn)生 HD wallet
先將 mnemonic code 轉(zhuǎn)成 binary 的 seed白胀。
var seed = bip39.mnemonicToSeed(mnemonic)
使用 seed 產(chǎn)生 HD Wallet椭赋。如果要說更明確,就是產(chǎn)生 Master Key 并記錄起來或杠。
var hdWallet = hdkey.fromMasterSeed(seed)
產(chǎn)生第一個 Ethereum Address
產(chǎn)生 Wallet 中第一個帳戶的第一組 keypair哪怔。可以從 Master Key向抢,根據(jù)其路徑 m/44'/60'/0'/0/0
推導(dǎo)出來认境。
var key1 = hdWallet.derivePath("m/44'/60'/0'/0/0")
使用 keypair 中的公鑰產(chǎn)生 address。
var address1 = util.pubToAddress(key1._hdkey._publicKey, true)
取得的 Address:
685ce4cbdd5c19b64ca008cb85b83947e5318efa
Encoding Address
Ethereum 很貼心挟鸠,為了避免大家打錯 address(導(dǎo)致把錢送錯人)叉信,Ethereum 讓 Address 變得比較難打?艘希!總之一般會用 EIP55: Mixed-case checksum address encoding 再進(jìn)行編碼茉盏。許多錢包也支援用戶輸入沒經(jīng)過編碼的 Address鉴未,那就會跳過 checksum 機(jī)制枢冤,建議還是使用編碼過的 Address鸠姨。
address1 = util.toChecksumAddress(address1.toString('hex'))
最后取得的 Address 會像:
0x685ce4CbDd5c19b64CA008cB85b83947e5318EFA
可以用 Mnemonic Code Converter 驗證結(jié)果
使用 Ethereum HD wallet
把 mnemonic code 記錄下來好好保存,就會是一個冷錢包(指不連網(wǎng)路的錢包核蘸,所以安全很多)巍糯。可以使用產(chǎn)生出來的 address 收 Ether 或任何 REC20 Token客扎。要送錢的話祟峦,可以匯入到任一個支援 Ethereum HD Wallet 的錢包。常用的 Ethereum HD wallet 像徙鱼,在瀏覽器使用的 MyEtherWallet宅楞、MetaMask 和在手機(jī)使用的 imToken 等。
題外話袱吆,MetaMask 如何在瀏覽器儲存我們的 mnemonic code?
相信大家都了解了厌衙,有 mnemonic code 就可以產(chǎn)生 HD Wallet 中所有的 keys。有了 keys 就可以任意送錢包中的 Ether 或 Token 給別人绞绒。所以 mnemonic code 很重要I粝!!蓬衡!那這么重要的東西保存在瀏覽器不會很危險嗎喻杈?我便研究下我常用的 MetaMask 瀏覽器錢包。MetaMask 將加密后的 mnemonic code 存在瀏覽器的 Local Storage(一塊只存在 Local 且不會過期的資料區(qū)塊)狰晚。加密使用用戶另外輸入的密碼筒饰,再匯入時會要求用戶設(shè)定密碼(如上圖),而每一次重新開啟錢包都會要求輸入密碼家肯。解密算法有 Open Source龄砰,也有線上 Live Demo。
References
其他相關(guān) Ethereum JavaScript 套件
感謝 Jiyi 大大提供密碼學(xué)專業(yè)知識讨衣,雖然詳細(xì)的數(shù)學(xué)計算本篇沒有提到换棚,但讓我有底氣的完成這篇文章。