比特幣錢包開發:透過助記詞擴充套件子地址的原理與編碼

買賣虛擬貨幣

目標

  1. 掌握生成助記詞的原理
  2. 掌握助記詞生成種子的原理
  3. 掌握種子生成子秘鑰的原理
  4. 程式設計實踐:從生成助記詞到子地址

前言

為了安全儘量保證比特幣地址的公鑰未在網路上出現過,這就需要我們每次支付時,將支付額轉到一個新的賬戶發起轉賬,而收款時使用一個新賬號地址。為了滿足這樣的場景,就需要安全管理很多的賬號與對應的秘鑰,這樣顯然是不科學的。因此,這裡講解透過助記詞生成很多子地址,這樣,我們只需儲存一份助記詞就相當於儲存好了2^31個賬號的私鑰。

一、助記詞的生成過程

BIP39是助記詞標準的實現,助記詞是一個隨機數的字序列,用來作為種子產生一個確定性的錢包。單詞序列足以重新建立種子,然後再建立錢包和所有派生的金鑰。一個實現帶有助記詞的確定性錢包應用程式在首次建立錢包時將向使用者顯示12到24個單詞的序列。該單詞序列是錢包備份,可用於恢復和重新建立相同或任何相容的錢包應用程式中的所有金鑰。記憶單詞使使用者更容易備份錢包,因為與隨機數字序列相比,它們易於閱讀和正確地轉錄。所有的助記詞在這裡可以檢視:助記詞詞庫。

使用BIP39中定義的標準化過程,錢包自動生成助記詞。錢包從一個熵源開始,新增一個校驗和,然後將熵對映到一個單詞列表,具體步驟如下:

  1. 建立128到256位的隨機序列(熵)。
  2. 透過獲取SHA256雜湊的第一(熵長度/ 32)位來建立隨機序列的校驗和。
  3. 將校驗和新增到隨機序列的末尾。
  4. 將序列分成11位的部分。
  5. 將每個11位值對映到來自2048個單詞的預定義字典中的單詞。
  6. 助記詞是單詞序列。

生成助記詞的步驟如下圖。

img

下表描述了初始熵長度(ENT),校驗和長度(CS)和單詞中生成的助記詞(MS)的長度之間的關係。

熵(bits)Checksum(bits)熵+Checksum(bits)助記詞長度(words)128413212160516515192619818224723121256826424

二、從助記詞到種子

使用者可以決定用密碼來保護他們的助記符。如果不存在密碼短語,則使用空字串“”。

助記詞代表長度為128到256位的熵。然後使用熵透過使用金鑰擴充套件函式PBKDF2來匯出更長(512位)的種子。然後,所產生的種子用於構建確定性錢包並獲得其金鑰。

金鑰擴充套件功能有兩個引數:助記詞和鹽(salt)。金鑰擴充套件功能中的鹽的目的是使得難以構建能夠進行暴力攻擊的查詢表。在BIP-39標準中,salt具有另一個目的 - 它允許引入密碼短語作為保護種子的額外安全因子。

  1. PBKDF2金鑰擴充套件功能的第一個引數是從步驟6產生的助記詞。
  2. PBKDF2金鑰擴充套件功能的第二個引數是salt。salt由字串常量“mnemonic”和可選的使用者提供的密碼短語字串組成。
  3. PBKDF2使用2048輪HMAC-SHA512雜湊演算法來擴充套件助記詞和salt引數,產生512位值作為其最終輸出。那個512位的值就是種子。

使用助記詞來生成種子的步驟如下圖。

img

下面演示一下助記詞生成種子的例項:

12個長度的助記詞,無密碼生成種子

9C286891-0902-4F1D-AD31-4EB804F71DF7

12個長度的助記詞,有密碼生成種子

51FC2016-15A7-4641-A203-43964671B881

24個長度的助記詞,無密碼生成種子

DE00BE18-E081-446B-9736-69198B2524E0

12個長度的助記詞,有密碼生成種子

442FFE9B-0929-4C4B-B0B1-7D046B5C85FC

三、種子生成子秘鑰

種子透過不可逆HMAC-SHA512演算法推算出512位的雜湊串,前256位是主私鑰Master Private Key (m),後256位是主鏈碼Master Chain Code(c)。

給定父擴充套件私鑰和索引i,可以計算相應的子擴充套件私鑰。

函式CKDpriv((kpar,cpar),i)→(ki,ci)

如果i ≥ 2^31(硬化的子金鑰):讓I= HMAC-SHA512(Key = cpar,Data = 0x00 || ser256(kpar)|| ser32(i))。 (注意:0x00將私鑰補齊到33位元組長。)

如果i<2^31(普通的子金鑰):讓I= HMAC-SHA512(Key = cpar,Data = serP(point(kpar))|| ser32(i))。

給定父擴充套件公鑰和索引i,可以計算相應的子擴充套件公鑰。它只針對未硬化的子金鑰定義。

如果i ≥ 2^31(硬化子金鑰):返回失敗

如果i<2^31(普通子金鑰):讓I= HMAC-SHA512(Key = cpar, Data = serP(Kpar) || ser32(i)).

擴充套件私鑰的字首是xprv,如:

tprv8iGPAfgu51nkCZZtua8jFgzVoCQLqHZrLCQonxTo7qdtzutL8ZFZt1yAtpcUF8sHdNyiVhece3SSRsBvtUCKpGkRvxXgV2TMdcDbKQzstta 擴充套件公鑰的字首是tpub,如:
tpubDExRK5j9DPUR62bgoDoKf6ecNDvGzckkuW1b5UW6Y7SHqQ96kx5A4Wb34w6bkHUStdq5w7ZHPQHkipwRdSQMbGnqTAQj1sEBaJmL9wXvBSu

每個擴充套件金鑰有 2^31 個普通子金鑰,2^31個硬化子金鑰。這些子金鑰都有一個索引,普通子金鑰使用索引0到2^31-1,硬化的子金鑰使用索引 2^31 到 2^32-1,為了簡化硬化金鑰索引的符號,數字iH表示i + 2^31。

以上過程再結合BIP43,BIP44,HD錢包就實現了多幣種、多賬戶、多用途等功能。

四、程式設計實踐:從生成助記詞到擴充套件子地址

程式碼

var bitcoin = require('bitcoinjs-lib');
var bip39 = require("bip39")
var bip32 = require("bip32")

const myNetwork = bitcoin.networks.testnet

const mnemonic = 'eternal list thank chaos trick paper sniff ridge make govern invest abandon' // const mnemonic = bip39.generateMnemonic() const seed = bip39.mnemonicToSeed(mnemonic, "lixu1234qwer") const root = bip32.fromSeed(seed, myNetwork)

for(var i = 0; i < 3; i++) {    const path = "m/44'/1'/0'/0/"+i    console.log("路徑:", path)    const keyPair = root.derivePath(path)

const privateKey = keyPair.toWIF()    console.log("私鑰", privateKey)

const publicKey = keyPair.publicKey.toString("hex")    console.log("公鑰:", publicKey)

let address = getAddress(keyPair, myNetwork)    console.log("地址:", address, "\n") }

function getAddress(keyPair, network) {    const { address } = bitcoin.payments.p2pkh({ pubkey: keyPair.publicKey , network:network})    return address }

輸出

  1. 下面是在測試網路下生成的私鑰、公鑰、地址

  1. 下面是在正式網路下生成的私鑰、公鑰、地址

驗證

程式碼解析

  • 需要安裝庫:bitcoinjs-lib、bip39、bip32。
  • bitcoin.networks.testnet:指定為測試網路,若切換到正式網路,則為bitcoin.networks.bitcoin,同時需要改變路徑。
  • bip39.generateMnemonic():用於生成助記詞。
  • bip39.mnemonicToSeed(mnemonic, "lixu1234qwer"):將助記詞與密碼轉成種子。
  • bip32.fromSeed(seed, myNetwork):將種子轉為相應網路下的root。
  • const path = "m/44'/1'/0'/0/"+i:指定第一個賬號的第i個擴充套件子賬號路徑。若切換到正式網路,路徑則是"m/44'/0'/0'/0/"+i
  • root.derivePath(path):獲取指定路徑的keyPair。
  • keyPair.toWIF():獲取私鑰。
  • keyPair.publicKey.toString("hex"):獲取公鑰。
  • const { address } = bitcoin.payments.p2pkh({ pubkey: keyPair.publicKey , network:network}):獲取普通比特幣地址,p2pkh這是最常見的比特幣交易地址型別。可見:在測試網路中的地址是以m或n開頭,在正式網路中是以1開頭。

免責聲明:

  1. 本文版權歸原作者所有,僅代表作者本人觀點,不代表鏈報觀點或立場。
  2. 如發現文章、圖片等侵權行爲,侵權責任將由作者本人承擔。
  3. 鏈報僅提供相關項目信息,不構成任何投資建議

推荐阅读

;