錢包

在比特幣中,"錢包"一詞用於描述幾個不同的東西。

從較高的角度來說,錢包是用戶使用的應用程式,控制對用戶資金的訪問,管理密鑰和地址,追蹤餘額以及創建和簽署交易。

更狹義地,從開發者的角度來看,"錢包"一詞是指用於儲存和管理用戶密鑰的資料結構。

在本章中,我們將看看第二個含義,即錢包是私鑰的容器,通常以結構化檔案或簡單資料庫的形式實現。

錢包技術概述

在本節中,我們總結了用於構建用戶友好,安全和靈活的比特幣錢包的各種技術。

關於比特幣的一個常見誤解是比特幣錢包包含比特幣。實際上,錢包只包含密鑰。"比特幣"被記錄在比特幣網路的區塊鏈中。用戶通過使用錢包中的密鑰簽署交易來控制網路上的硬幣。從某種意義上說,比特幣錢包是一個 密鑰串 keychain

Tip

比特幣錢包包含鑰匙,而不是硬幣。每個用戶都有一個包含密鑰的錢包。錢包真的是包含私鑰/公鑰的鑰匙串 (參見 [private_public_keys]). 用戶使用密鑰簽署交易,從而證明他們擁有交易的輸出(他們的比特幣)。比特幣以交易輸出的形式(通常記作vout或txout)儲存在區塊鏈中。

根據包含的密鑰是否彼此相關劃分,主要有兩種類型的錢包。

第一種是 非確定性錢包 nondeterministic wallet ,其中每個密鑰都是從隨機數中獨立生成的。密鑰不相互關聯。這種類型的錢包也被稱JBOK錢包(Just a Bunch Of Keys)。

第二種是 確定性錢包 deterministic wallet,其中所有密鑰都來自單個主密鑰,稱為 種子 seed 。這種錢包中的所有密鑰都是相互關聯的,如果有原始種子,可以再次生成。確定性錢包中使用了許多的 密鑰派生 key derivation 方法。 最常用的派生方法使用樹狀結構,並稱為 分層確定性 hierarchical deterministic 錢包或HD錢包。

確定性錢包是從種子初始化的。為了使這些更容易使用,種子被編碼為英文單詞,也被稱為助記詞 mnemonic code words

接下來的幾節將從較高的角度介紹這些技術。

非確定性(隨機)錢包

在第一個比特幣錢包(現在稱為Bitcoin Core)中,錢包是隨機生成的私鑰集合。例如,Bitcoin Core客戶端首次啟動時生成100個隨機私鑰,並根據需要生成更多的密鑰,每個密鑰只使用一次。這些錢包正在被確定性的錢包取代,因為它們的管理,備份和匯入很麻煩。隨機密鑰的缺點是,如果你生成了很多密鑰,你必須保留所有密鑰的副本,這意味著錢包必須經常備份。每個密鑰都必須備份,否則,如果錢包變得不可用,則其控制的資金將不可撤銷地丟失。這與避免地址重用的原則直接衝突,即每個比特幣地址僅用於一次交易。地址重用將多個交易和地址相互關聯來,會減少隱私。 0型非確定性錢包是窮人的選擇,如果你想避免地址重用,就要管理許多密鑰,頻繁備份。儘管Bitcoin Core客戶端包含0型錢包,但Bitcoin Core開發人員不鼓勵使用此錢包。 Type-0 nondeterministic (random) wallet: a collection of randomly generated keys 展示了一個非確定性錢包,它包含一個鬆散的隨機密鑰集合。

Tip

除了簡單的測試以外,不推薦使用非確定性錢包,備份和使用起來太麻煩了。請使用基於行業標準的有 助記詞 HD wallet 進行備份。

Non-Deterministic Wallet
Figure 1. Type-0 nondeterministic (random) wallet: a collection of randomly generated keys

Deterministic (Seeded) Wallets

確定性的,或"基於種子的"錢包是包含私鑰的錢包,這些私鑰都是通過使用單向雜湊函數從公共種子派生的。種子是隨機生成的數字,與其他數據(如索引編號或"chain code"(參見​分層確定性錢包(HD Wallets)(BIP-32/BIP-44)))組合以匯出私鑰。在確定性錢包中,種子足以恢復所有的派生密鑰,因此在創建時一次備份就足夠了。種子對於錢包匯出或匯入也是足夠的,允許在不同的錢包實施之間輕鬆遷移所有用戶的密鑰。Type-1 deterministic (seeded) wallet: a deterministic sequence of keys derived from a seed 展示了確定性錢包的邏輯圖。

Deterministic Wallet
Figure 2. Type-1 deterministic (seeded) wallet: a deterministic sequence of keys derived from a seed

分層確定性錢包(HD Wallets)(BIP-32/BIP-44)

確定性錢包的開發使得從單個"種子"中獲得許多密鑰變得容易。確定性錢包的最高級形式是由BIP-32標準定義的HD錢包。HD錢包包含以樹結構匯出的密鑰,父密鑰可以匯出一系列的子密鑰,每個子密鑰可以匯出一系列孫子密鑰等等,可達到無限深度。這個樹結構在Type-2 HD wallet: a tree of keys generated from a single seed中進行了說明。

HD wallet
Figure 3. Type-2 HD wallet: a tree of keys generated from a single seed

與隨機(非確定性)密鑰相比,HD錢包具有兩大優勢。首先,樹結構可以用來表達額外的組織含義,例如,使用子密鑰的特定分支來接收傳入的支付,使用另一個分支來接收支付時的零錢。分支的密鑰也可用於組織機構設置,將不同分支分配給部門,子公司,特定功能或會計類別。

HD錢包的第二個優點是用戶可以創建一系列公鑰而無需訪問相應的私鑰。這允許HD錢包用於不安全的伺服器或僅作為接收用途,為每次交易發出不同的公鑰。公鑰不需要事先預加載或派生,伺服器也沒有可以花費資金的私鑰。

種子和助記詞 (BIP-39)

HD錢包是管理許多密鑰和地址的非常強大的機制。如果將它們與標準化方式相結合,從一系列易於轉錄,匯出和跨錢包匯入的英語單詞創建種子,就更加有用。這被稱為 助記 ,BIP-39定義了這個標準。今天,大多數比特幣錢包(以及用於其他密碼貨幣的錢包)都使用此標準,並且可以使用可互操作的助記詞匯入和匯出種子以進行備份和恢復。

我們實際來看一下。下列哪類種子更易於轉錄,在紙上記錄,無誤地讀取,匯出/匯入另一個錢包?

A seed for an deterministic wallet, in hex
0C1E24E5917779D297E14D45F14E1A1A
A seed for an deterministic wallet, from a 12-word mnemonic
army van defense carry jealous true
garbage claim echo media make crunch

錢包最佳實踐

隨著比特幣錢包技術的成熟,出現了一些常見的行業標準,使比特幣錢包具有廣泛的互操作性,易用性,安全性和靈活性。這些通用標準是:

  • 助記詞(mnemonic code words), 基於BIP-39

  • 分層確定性錢包(HD wallets), 基於BIP-32

  • 多用途分層確定性結構(Multipurpose HD wallet structure), 基於BIP-43

  • 多幣種和多帳戶錢包(Multicurrency and multiaccount wallets),基於BIP-44

這些標準可能會改變或因未來的發展而過時,但現在它們形成了一系列連鎖技術,這些技術已成為比特幣事實上的錢包標準。

這些標準已被軟體和硬體比特幣錢包廣泛採用,使所有這些錢包可以互操作。用戶可以匯出其中一個錢包上生成的助記詞並將其匯入另一個錢包,恢復所有交易,密鑰和地址。

支援這些標準的軟體錢包的一些例子包括(按字母順序排列)Breadwallet,Copay,Multibit HD和Mycelium。支援這些標準的硬體錢包的例子包括(按字母順序列出)Keepkey,Ledger和Trezor。

以下各節詳細介紹這些技術。

Tip

如果你正在實施比特幣錢包,則應按照BIP-32,BIP-39,BIP-43和BIP-44標準,將其構建為HD錢包,並將種子編碼為助記詞用於備份,就像以下章節介紹的那樣。

使用比特幣錢包

[user-stories] 中我們介紹了Gabriel, 一位在里約熱內盧的富有進取精神的年輕人,他正在經營一家簡單的網上商店,銷售比特幣品牌的T恤,咖啡杯和貼紙。

Gabriel 使用 Trezor 比特幣硬體錢包 (A Trezor device: a bitcoin HD wallet in hardware) 安全地管理他的比特幣。Trezor是一個有兩個按鈕的簡單的USB設備,用於儲存密鑰(以HD錢包的形式) ,簽署交易。Trezor錢包實現了本章介紹的所有工業標準,因此Gabriel並不依賴任何專有技術或單一供應商解決方案。

alt
Figure 4. A Trezor device: a bitcoin HD wallet in hardware

當Gabriel首次使用Trezor時,該設備通過內置硬體隨機數生成器生成助記符和種子。在這個初始化階段,錢包在螢幕上逐一顯示帶有編號的單詞序列(參見 Trezor displaying one of the mnemonic words)。

Trezor wallet display of mnemonic word
Figure 5. Trezor displaying one of the mnemonic words

記錄下助記詞,Gabriel可以在他的Trezor設備丟失或損壞時使用備份的助記詞進行恢復。這種助記詞可以用於新的Trezor設備或任意一個兼容的軟體或硬體錢包。請注意,助記詞的順序很重要。

Table 1. Gabriel’s paper backup of the mnemonic

1.

army

7.

garbage

2.

van

8.

claim

3.

defense

9.

echo

4.

carry

10.

media

5.

jealous

11.

make

6.

true

12.

crunch

Note

為簡單起見,在 Gabriel’s paper backup of the mnemonic 中展示了12個助記詞。實際上,大多數硬體錢包可以產生更安全的24個助記詞。不管長度如何,助記詞的使用方式完全相同。

對於第一次網店實踐,Gabriel使用Trezor設備上生成的單個比特幣地址。所有客戶都可以使用這個地址進行所有訂單。正如我們將看到的,這種方法有一些缺點,可以通過HD錢包進行改進。

錢包技術細節

現在我們來仔細研究比特幣錢包所使用的每個重要行業標準。

助記詞(Mnemonic Code Words)(BIP-39)

助記詞是表示(編碼)用作派生確定性錢包的種子的隨機數的一個單詞序列。單詞序列足以重新創建種子,並重新創建錢包和所有派生的密鑰。使用助記詞實現確定性錢包的錢包應用會在首次創建錢包時向用戶顯示12至24個單詞的序列。這個單詞序列是錢包的備份,可用於在相同或任何兼容的錢包應用中恢復和重新創建所有密鑰。與隨機數字序列相比,助記詞使得用戶更容易備份錢包,因為它們易於閱讀和正確轉錄。

Tip

助記詞通常與"大腦錢包(brainwallets)"混淆。他們不一樣。主要區別在於大腦錢包由用戶選擇的單詞組成,而助記詞由錢包隨機創建並呈現給用戶。這個重要的區別使助記詞更加安全,因為人類是非常貧乏的隨機性來源。

助記詞在BIP-39中定義(參見[appdxbitcoinimpproposals])。注意,BIP-39是助記詞標準的一個實現。還有一個不同的標準,使用一組不同的詞,在BIP-39之前由Electrum錢包使用。 BIP-39由生產Trezor硬體錢包的公司提出,與Electrum不兼容。但是,BIP-39現在已經獲得了廣泛的行業支援,數十種產品可以互操作,被視為事實上的行業標準。

BIP-39定義了助記詞和種子的創建方法,我們通過九個步驟來描述它。為了清楚起見,該過程分為兩部分:步驟1至6在 [generate_mnemonic_words] 中,步驟7至9在 從助記詞到種子 中。

生成助記詞

助記詞是由錢包使用BIP-39中定義的標準化過程自動生成的。錢包從一個熵源開始,添加校驗碼,將熵映射到單詞列表:

  1. 創建一個128到256位的隨機序列(熵)。

  2. 通過取其SHA256雜湊的第一個(熵長度/ 32)位創建隨機序列的校驗碼。

  3. 將校驗碼添加到隨機序列的末尾。

  4. 將結果拆分為11位長的多個段。

  5. 將每個11位值映射到有2048個單詞的預定義字典中的一個單詞。

  6. 助記詞就是這些單詞的序列。

Generating entropy and encoding as mnemonic words 展示瞭如何使用熵來生成助記詞。

Generating entropy and encoding as mnemonic words
Figure 6. Generating entropy and encoding as mnemonic words

Mnemonic codes: entropy and word length 顯示了熵數據的大小與助記詞的長度之間的關係。

Table 2. Mnemonic codes: entropy and word length
Entropy (bits) Checksum (bits) Entropy + checksum (bits) Mnemonic length (words)

128

4

132

12

160

5

165

15

192

6

198

18

224

7

231

21

256

8

264

24

從助記詞到種子

助記詞表示長度為128到256位的熵。然後使用熵通過使用密鑰擴展函數PBKDF2來匯出更長的(512位)種子。之後使用生成的種子構建確定性錢包並匯出其密鑰。

密鑰擴展函數需要兩個參數:助記詞和 salt 。在密鑰擴展函數中使用鹽的目的是使構建一個查找表並暴力破解難以實現。在BIP-39標準中,鹽有另一個目的 - 它允許引入密碼,作為保護種子的附加安全因素,我們將在 BIP-39中可選的密碼中詳細描述。

步驟7到9中描述的過程從 [generated_mnemonic_words] 中的過程繼續:

  1. PPBKDF2密鑰擴展函數的第一個參數是步驟6中產生的 助記詞
  2. PPBKDF2密鑰擴展函數的第一個參數是 鹽(salt) 。鹽由字串 "mnemonic" 加上可選的用戶提供的密碼組成。
  3. PBKDF2使用HMAC-SHA512演算法執行2048輪雜湊來擴展助記詞和鹽,產生一個512位值,就是種子。

From mnemonic to seed 展示瞭如何使用助記詞來生成種子。

From mnemonic to seed
Figure 7. From mnemonic to seed
Tip

密鑰擴展方法及其2048輪雜湊是一種非常有效的防止對助記詞或密碼短語攻擊的保護。它使得嘗試超過幾千個密碼和助記詞組合的成本非常高,而可能派生的種子數量很大(2512)。

表格 #mnemonic_128_no_pass, #mnemonic_128_w_pass, and #mnemonic_256_no_pass 顯示一些助記詞和他們產生的種子(沒有任何密碼)的例子。

Table 3. 128-bit entropy mnemonic code, no passphrase, resulting seed

Entropy input (128 bits)

0c1e24e5917779d297e14d45f14e1a1a

Mnemonic (12 words)

army van defense carry jealous true garbage claim echo media make crunch

Passphrase

(none)

Seed (512 bits)

5b56c417303faa3fcba7e57400e120a0ca83ec5a4fc9ffba757fbe63fbd77a89a1a3be4c67196f57c39 a88b76373733891bfaba16ed27a813ceed498804c0570

Table 4. 128-bit entropy mnemonic code, with passphrase, resulting seed

Entropy input (128 bits)

0c1e24e5917779d297e14d45f14e1a1a

Mnemonic (12 words)

army van defense carry jealous true garbage claim echo media make crunch

Passphrase

SuperDuperSecret

Seed (512 bits)

3b5df16df2157104cfdd22830162a5e170c0161653e3afe6c88defeefb0818c793dbb28ab3ab091897d0 715861dc8a18358f80b79d49acf64142ae57037d1d54

Table 5. 256-bit entropy mnemonic code, no passphrase, resulting seed

Entropy input (256 bits)

2041546864449caff939d32d574753fe684d3c947c3346713dd8423e74abcf8c

Mnemonic (24 words)

cake apple borrow silk endorse fitness top denial coil riot stay wolf luggage oxygen faint major edit measure invite love trap field dilemma oblige

Passphrase

(none)

Seed (512 bits)

3269bce2674acbd188d4f120072b13b088a0ecf87c6e4cae41657a0bb78f5315b33b3a04356e53d062e5 5f1e0deaa082df8d487381379df848a6ad7e98798404

BIP-39中可選的密碼

BIP-39標準允許在派生種子中使用可選的密碼。如果沒有使用密碼,助記詞將被一個常量字串 mnemonic 的鹽擴展,產生一個特定的512位種子。如果使用密碼短語,則擴展函數會從同一助記符中生成一個 不同的 種子。對於一個助記詞,每一個可能的密碼都會導致不同的種子。本質上,沒有 "錯誤的" 密碼。所有密碼都是有效的,會生成不同的種子,形成一大批未初始化的錢包。可能的錢包的集合非常大(2512),因此沒有可能暴力破解或意外猜測出正在使用的錢包。

Tip

BIP-39中沒有 "錯誤的" 口令。每個密碼都會導致一些錢包,除非以前使用過,錢包將是空的。

可選的密碼引入了兩個重要功能:

  • 第二重保護,需要記憶的密碼使得只獲得助記詞沒有用,避免助記詞被盜時的損失。

  • 一種似是而非的拒絕形式或"脅迫錢包",一個選定的密碼會導致進入一個帶有少量資金的錢包,用於將攻擊者的注意力從有大部分資金的"真實"錢包引開。

但是,要注意使用密碼也會導致丟失的風險:

  • 如果錢包所有者無行為能力或死亡,而且沒有其他人知道密碼,則種子無用,錢包中儲存的所有資金都將永久丟失。

  • 相反,如果所有者在與種子相同的位置備份密碼,它將失去第二重保護的意義。

雖然密碼非常有用,但應該結合精心策劃的備份和恢復過程,需要考慮主人是否存活,要允許其家人恢復密碼貨幣資產。

使用助記詞

BIP-39有許多不同的程式語言庫實現:

python-mnemonic

提出BIP-39標準的SatoshiLabs團隊用Python寫的參考實現

bitcoinjs/bip39

BIP-39的JavaScript實現,是流行的bitcoinJS框架的一部分。

libbitcoin/mnemonic

BIP-39的C++實現,是流行的Libbitcoin框架的一部分。

還有一個在網頁中實現的BIP-39生成器,這對於測試非常有用。 A BIP-39 generator as a standalone web page 展示了生成助記詞,種子和擴展私鑰的網頁。

BIP-39 generator web-page
Figure 8. A BIP-39 generator as a standalone web page

這個頁面 (https://iancoleman.github.io/bip39/) 可以離線或在線訪問

通過種子創建HD錢包

HD錢包是由一個 根種子 root seed 創建的,是一個128位,256位或512位的隨機數。通常,這個種子是從 助記詞 mnemonic 生成的,詳見前一節。

HD錢包中的每個密鑰都是從這個根種子確定性地派生出來的,這使得可以在任何兼容的HD錢包中從該種子重新創建整個HD錢包。這使得備份,恢復,匯出和匯入包含數千乃至數百萬個密鑰的HD錢包變得很容易,只需傳輸根種子的助記詞即可。

創建 主密鑰 master keys 和主鏈碼 master chain code 的過程如 Creating master keys and chain code from a root seed 所示。

HDWalletFromRootSeed
Figure 9. Creating master keys and chain code from a root seed

將根種子作為 HMAC-SHA512 演算法的輸入,生成的雜湊結果用來生成 主私鑰 master private key (m) 和 主鏈碼 master chain code (c)。

然後使用我們在 [pubkey] 中看到的橢圓曲線乘法 m * G 利用主密鑰(m)生成相應的主公鑰(M)。

主鏈碼(c)用於在從父鍵創建子鍵的函數中引入熵,我們將在下一節看到。

子私鑰的派生

HD錢包使用 子密鑰派生 child key derivation (CKD) 方法從父密鑰派生子密鑰。

子密鑰派生方法基於單向雜湊函數,該函數結合:

  • 一個父級私鑰或公鑰 (ECDSA未壓縮密鑰)

  • 一個稱作鏈碼(chain code)的種子(256 bits)

  • 一個索引數字(32 bits)

鏈碼用於向過程中引入確定性隨機數據,所以只知道索引和子密鑰不足以派生其他子密鑰。除非有鏈碼,否則知道一個子鑰匙不能找到它的兄弟姐妹。初始鏈碼種子(樹的根部)由種子製成,而後續子鏈碼則從每個父鏈碼中匯出。

這三項(父密鑰,鏈碼和索引)被組合並雜湊以生成子鍵,如下所示。

使用HMAC-SHA512演算法將父公鑰,鏈碼和索引組合並雜湊,以產生512位雜湊。這個512位雜湊平分為兩部分。右半部分256位作為後代的鏈碼,左半部分256位被添加到父私鑰以生成子私鑰。在 Extending a parent private key to create a child private key 中,我們看到這個例子中的索引設置為0,以產生父項的"零"級(第一個索引)孩子。

ChildPrivateDerivation
Figure 10. Extending a parent private key to create a child private key

更改索引允許我們擴展父項並創建序列中的其他子項,例如Child 0,Child 1,Child 2等。每個父項可以有 2,147,483,647(2 31)個子項(232 範圍的一半 231是可用的,另一半保留用於特殊類型的推導,我們將在本章後面討論)。

在樹的下一層重複這個過程,每個孩子都可以成為父項並在無限的世代中創造自己的孩子。

使用派生的子密鑰

子私鑰與非確定性(隨機)密鑰沒有區別。因為派生函數是單向函數,不能使用子項來尋找父項和尋找任何兄弟姐妹。不能通過第n個子項找到它的兄弟姐妹,如第 n-1 個子項或者第 n+1 個子項,或者任何這個序列上的子項。只能通過父密鑰和鏈碼派生所有的孩子。如果沒有子鏈碼,子密鑰也不能派生任何孫項。你需要子私鑰和子鏈碼來啟動一個新分支並派生孫項。

那麼,子私鑰能用來幹什麼呢?它可以用來製作公鑰和比特幣地址。然後,它可以用來簽署交易,並花費任何支付給該地址的費用。

Tip

子私鑰,相應的公鑰和比特幣地址都與隨機創建的密鑰和地址沒有區別。在創建它們的HD錢包之外是不知道它們屬於一個序列的。一旦創建,就像"普通"鍵一樣工作。

擴展密鑰

如我們所見,基於三個輸入:密鑰,鏈碼和所需子項的索引,可以使用密鑰派生函數在樹的任何級別創建子項。這兩個基本要素是密鑰和鏈式程式碼,它們的組合稱為 擴展密鑰 extended key 。也可以認為"擴展密鑰"是"可擴展的密鑰",因為這樣的密鑰可以用來派生孩子。

擴展密鑰簡單地表示為由256位的密鑰和256位的鏈碼串聯成的512位序列。有兩種類型的擴展密鑰:擴展私鑰是私鑰和鏈碼的組合,可用於派生子私鑰(從它們產生子公鑰);擴展公鑰是公鑰和鏈碼,可用於創建子公鑰( 只有子公鑰 ),如 [public_key_derivation] 中所述。

將擴展密鑰視為HD錢包樹形結構中分支的根。可以通過分支的根,派生出其他分支。擴展私鑰可以創建一個完整的分支,而擴展公鑰只能創建一個公鑰分支。

Tip

擴展密鑰由私鑰或公鑰和鏈碼組成。擴展密鑰可以創建子項,在樹結構中生成自己的分支。共享一個擴展密鑰可以訪問整個分支。

擴展密鑰使用Base58Check編碼,可以輕鬆匯出匯入BIP-32兼容的錢包。擴展密鑰的Base58Check編碼使用特殊的版本號,當使用Base58字符進行編碼時,其前綴為"xprv"和"xpub",以使其易於識別。因為擴展的密鑰是512或513位,所以它比我們以前見過的其他Base58Check編碼的字串要長得多。

這是一個Base58Check編碼的擴展私鑰:

xprv9tyUQV64JT5qs3RSTJkXCWKMyUgoQp7F3hA1xzG6ZGu6u6Q9VMNjGr67Lctvy5P8oyaYAL9CAWrUE9i6GoNMKUga5biW6Hx4tws2six3b9c

這是對應的Base58Check編碼的擴展公鑰:

xpub67xpozcx8pe95XVuZLHXZeG6XWXHpGq6Qv5cmNfi7cS5mtjJ2tgypeQbBs2UAR6KECeeMVKZBPLrtJunSDMstweyLXhRgPxdp14sk9tJPW9
子公鑰派生

如前所述,HD錢包的一個非常有用的特性是能夠從父公鑰中獲得子公鑰,而沒有私鑰。這為我們提供了兩種派生子公鑰的方法:從子私鑰或直接從父公鑰獲取子公鑰。

因此,可以使用擴展公鑰,匯出HD錢包該分支中的所有 公鑰(注意只有公鑰)。

此快捷方式可用於創建非常安全的公鑰 - 只有部署伺服器或應用程式具有擴展公鑰的副本,並且沒有任何私鑰。這種部署可以產生無限數量的公鑰和比特幣地址,但無法花費發送到這些地址的任何資金。與此同時,在另一個更安全的伺服器上,擴展私鑰可以匯出所有相應的私鑰來簽署交易並花費金錢。

這個解決方案的一個常見應用是在提供電子商務應用程式的Web伺服器上安裝擴展公鑰。網路伺服器可以使用公鑰匯出函數來為每個交易(例如,為顧客購物車)創建新的比特幣地址。 Web伺服器上不會有任何易被盜的私鑰。沒有HD錢包,唯一的方法就是在單獨的安全伺服器上生成數千個比特幣地址,然後將其預先加載到電子商務伺服器上。這種方法很麻煩,需要不斷的維護以確保電子商務伺服器不會"用完"密鑰。

另一個常見應用是用於冷儲存或硬體錢包。在這種情況下,擴展私鑰可以儲存在紙錢包或硬體設備(如Trezor硬體錢包)上,而擴展公鑰可以保持在線。用戶可以隨意創建"接收"地址,而私鑰可以安全地在離線狀態下儲存。為了花費資金,用戶可以在離線簽名比特幣客戶端使用擴展私鑰簽名,或在硬體錢包設備上簽名交易(例如Trezor)。 Extending a parent public key to create a child public key 演示了用擴展父公鑰派生子公鑰的機制。

ChildPublicDerivation
Figure 11. Extending a parent public key to create a child public key

在網店中使用擴展公鑰

讓我們看看如何使用HD錢包繼續Gabriel的網上商店故事。

Gabriel 首先出於愛好建立了他的網上商店,基於簡單的Wordpress。他的商店非常簡單,只有幾個頁面和有一個比特幣地址的下單表單。

Gabriel 使用他的Trezor設備生成的第一個比特幣地址作為他的商店的主要比特幣地址。這樣,所有收到的付款都將支付給他的Trezor硬體錢包所控制的地址。

客戶將使用表單提交訂單並將支付款項發送至Gabriel發佈的比特幣地址,觸發一封電子郵件,其中包含Gabriel要處理的訂單詳情。每週只有幾個訂單,這個系統運行得很好。

然而,這家小型網上商店變得非常成功,吸引了當地的許多訂單。不久,Gabriel 便不知所措了。由於所有訂單都支付相同的地址,很難正確匹配訂單和交易,尤其是當同一數量的多個訂單緊密結合在一起時。

Gabriel 的 HD 錢包通過在不知道私鑰的情況下派生子公鑰的能力提供了更好的解決方案。Gabriel 可以在他的網站上加載一個擴展公鑰(xpub),用來為每個客戶訂單派生一個唯一的地址。Gabriel 可以從他的Trezor花費資金,但在網站上加載的 xpub 只能生成地址並獲得資金。HD錢包的這個特點是一個很好的安全功能。Gabriel 的網站不包含任何私鑰,因此不需要高度的安全性。

Gabriel將Web軟體與Trezor硬體錢包一起使用匯出xpub。必須插入Trezor設備才能匯出公鑰。請注意,硬體錢包永遠不會匯出私鑰 —— 這些密鑰始終保留在設備上。 Exporting an xpub from a Trezor hardware wallet 展示了Gabriel用於匯出xpub的Web界面。

Exporting the xpub from the Trezor
Figure 12. Exporting an xpub from a Trezor hardware wallet

Gabriel將 xpub 複製到他的網上商店的比特幣商店軟體中。並使用 Mycelium Gear ,這是一個開源的網上商店插件,用於各種網站託管和內容平臺。 Mycelium Gear使用 xpub 為每次購買生成一個唯一的地址。

強化的子密鑰派生

從 xpub 派生公鑰的分支是非常有用的,但有潛在的風險。訪問 xpub 不會訪問子私鑰。但是,因為 xpub 包含鏈碼,所以如果某個子私鑰已知,或者以某種方式洩漏,則可以與鏈式程式碼一起使用,派生所有其他子私鑰。一個洩露的子私鑰和一個父鏈碼可以生成所有其他的子私鑰。更糟的是,可以使用子私鑰和父鏈碼來推導父私鑰。

為了應對這種風險,HD錢包使用一種稱為 hardened derivation 的替代派生函數,該函數"破壞"父公鑰和子鏈碼之間的關係。強化派生函數使用父私鑰來派生子鏈碼,而不是父公鑰。這會在父/子序列中創建一個"防火牆",鏈碼不能危害父級或同級的私鑰。父私鑰替代父公鑰作為雜湊函數的輸入,強化後的派生函數看起來與正常的子私鑰派生幾乎相同,如 Hardened derivation of a child key; omits the parent public key 中的圖所示。

ChildHardPrivateDerivation
Figure 13. Hardened derivation of a child key; omits the parent public key

當使用強化的私有派生函數時,生成的子私鑰和鏈碼與正常派生函數所產生的完全不同。由此產生的"分支"密鑰可用於生成不易受攻擊的擴展公鑰,因為它們所包含的鏈碼不能用於揭示任何私鑰。因此,強化派生用於在繼承樹上使用擴展公鑰的級別之上創建"屏障"。

簡而言之,如果你想使用 xpub 的便利性來派生分支公鑰,而不想面臨洩漏鏈碼的風險,應該從強化的父項派生。作為最佳實踐,主密鑰的1級子密鑰始終使用強化派生,以防止主密鑰受到破壞。

常規派生與強化派生的索引號

在派生函數中使用的索引號是一個32位整數。為了便於區分通過常規推導函數派生的密鑰與通過強化派生派生的密鑰,該索引號分為兩個範圍。 0到231 - 1(0x0到0x7FFFFFFF)之間的索引號僅用於常規推導。 231 和 232 - 1(0x80000000到0xFFFFFFFF)之間的索引號僅用於硬化派生。因此,如果索引號小於231,則子密鑰是常規的,而如果索引號等於或大於 231,則子密鑰是強化派生的。

為了使索引號碼更容易閱讀和顯示,強化子密鑰的索引號從零開始顯示,但帶有一個符號。第一個常規子密鑰表示成0,第一個強化子祕鑰( 索引號是 0x80000000 )表示成0'。以此類推,第二個強化子密鑰( 0x80000001 ) 表示成1'。當你看到HD錢包索引i'時,它表示231+i.

HD錢包密鑰標識符 (path)

HD錢包中的密鑰使用"路徑(path)"命名約定來標識,樹的每個級別都用斜槓(/)字符分隔(請參見 HD wallet path examples)。從主密鑰派生的私鑰以"m"開頭。從主公鑰派生的公鑰以"M"開始。因此,主私鑰的第一個子私鑰為 m/0。第一個子公鑰是 M/0。第一個子私鑰的第二個子私鑰是 m/0/1,依此類推。

從右向左讀取一個密鑰的"祖先",直到到達派生出它的主密鑰。例如,標識符 m/x/y/z 描述了私鑰 m/x/y 的第z個子私鑰,m/x/y 是私鑰 m/x 的第y個子私鑰,m/x 是 m 的第x個子私鑰。

Table 6. HD wallet path examples
HD path Key described

m/0

The first (0) child private key from the master private key (m)

m/0/0

The first grandchild private key from the first child (m/0)

m/0'/0

The first normal grandchild from the first hardened child (m/0')

m/1/0

The first grandchild private key from the second child (m/1)

M/23/17/0/0

The first great-great-grandchild public key from the first great-grandchild from the 18th grandchild from the 24th child

HD錢包的樹狀結構導航

HD錢包的樹狀結構提供了巨大的靈活性。每個父級擴展密鑰的可以有40億個子密鑰:20個常規子密鑰和20億強化子密鑰。這些子密鑰中的每一個又可以有另外40億子密鑰。這棵樹像你想要的一樣深,有無限的世代。然而,這些靈活性,導致在這個無限樹中導航變得非常困難。在不同實現之間轉移HD錢包尤其困難,因為內部分支和子分支的可能性是無窮無盡的。

有兩個BIP為HD錢包的樹狀結構提出了一些建議的標準,為這種複雜性提供解決方案。BIP-43建議使用第一個強化子索引作為表示樹狀結構"用途"的特殊標識符。基於BIP-43,HD錢包應該只使用樹的一個1級分支,索引號通過定義其用途來標識樹的其餘部分的結構和名稱空間。例如,僅使用分支 m/i'/ 的HD錢包表示特定用途,用途由索引號"i"標識。

BIP-44在BIP-43下提出了一個多帳戶結構作為"用途"號碼 44' 。所有遵守BIP-44的HD錢包通過僅使用樹的一個分支來體現:m/44'/。

BIP-44定義了包含五個預定義樹級的結構:

m / purpose' / coin_type' / account' / change / address_index

第一級 "用途" 始終設置為 44',第二級 "coin_type" 表示密碼貨幣的類型,以支援多貨幣HD錢包,其中每種貨幣在第二級下具有其自己的子樹。現在定義了三種貨幣:比特幣是 m/44'/0',比特幣測試網是m/44'/1',萊特幣(Litecoin)是 m/44'/2'。

樹的第三層是"帳戶",允許用戶將他們的錢包細分為單獨的邏輯子帳戶,以用於會計或組織目的。例如,一個HD錢包可能包含兩個比特幣"賬戶":m/44'/0'/0' 和 m/44'/0'/1'。每個帳戶都是自己的子樹的根。

在第四層,"零錢",HD錢包有兩個子樹,一個用於創建接收地址,另一個用於創建零錢地址。請注意,雖然以前的層級使用強化派生,但此層級使用常規派生。這是為了允許樹的這個級別匯出擴展的公鑰以供在不安全的環境中使用。"地址_索引"由HD錢包的第四級派生,也就是第五級。例如,主賬戶中比特幣支付的第三個接收地址為 M/44'/0'/0'/0/2。 BIP-44 HD wallet structure examples 顯示了幾個例子。

Table 7. BIP-44 HD wallet structure examples
HD path Key described

M/44'/0'/0'/0/2

主要比特幣賬戶的第三個接收地址公鑰

M/44'/0'/3'/1/14

第四個比特幣帳戶的第十五個零錢地址公鑰

m/44'/2'/0'/0/1

Litecoin主賬戶中的第二個私鑰,用於簽署交易