1. 程式人生 > >比特幣開發者指南(6)--錢包

比特幣開發者指南(6)--錢包

錢包

比特幣錢包可以指代錢包程式或錢包檔案。電子錢包程式建立公共金鑰接收satoshis,並使用相應的私鑰花費satoshis 。電子錢包檔案儲存私鑰和(可選的)與錢包程式的交易相關的其他資訊。

錢包程式和錢包檔案在下面的不同小節中介紹,本文件試圖澄清我們在談論是錢包程式或錢包檔案。

錢包軟體

允許satoshis的接收和支出是錢包軟體的唯一基本特徵,但是特定的錢包程式不需都要做這兩件事情。兩個錢包程式可以一起工作,一個程式分發公鑰以便接收satoshis,另一個程式簽名交易來花費那些 satoshis。

錢包程式還需要與P2P網路進行互動,以從塊鏈獲取資訊並廣播新的交易。然而,分發公鑰或簽名交易的程式就不需要與P2P網路本身進行互動。

這使我們有一個錢包系統的三個必要但可分離的部分:一個公鑰分發程式,簽名程式和聯網程式。在下面的小節中,我們將介紹這些部分的常見組合。

注意:我們講通常是分發公鑰。在許多情況下,將分發P2PKH或P2SH雜湊來代替公鑰,實際的公鑰僅在它們控制的輸出被花費時候分發。

全服務的錢包

最簡單的錢包是一個執行所有三個功能的程式:它產生私鑰,匯出相應的公鑰,根據需要幫助分發公鑰,監視花費在公鑰上的輸出,建立並簽名花費那些輸出的交易,並廣播簽名過的交易。


Full-Service Wallets

在撰寫本文時,幾乎所有流行的錢包可用作全服務錢包。

全方位服務錢包的主要優點是易於使用。一個程式執行使用者需要接收的所有東西,並花費satoshis。

全服務錢包的主要缺點是它們在連線到網際網路的裝置上儲存私鑰。這種裝置被入侵是常見的事情,因特網連線使得容易將私鑰從被感染的裝置傳送給攻擊者。

為了防盜,許多錢包程式為使用者提供可選項加密包含私鑰的錢包檔案。當私鑰不被使用時,它可以提供保護,但是它不能防止捕獲金鑰的攻擊或從記憶體中讀取解密金鑰。

只簽名錢包

為了提高安全性,可以通過在更安全的環境中執行的單獨的錢包程式來生成和儲存私鑰。這些僅簽名的錢包與與對等網路互動的網路錢包配合使用。

典型的只簽名錢包軟體使用確定的金鑰產生器(稍後描述)去建立父私鑰公鑰,它們可以建立新的子私鑰公鑰。


Signing-Only Wallets

首次執行時,只簽名的錢包建立一個父私鑰,並將相應的父公鑰傳送到聯網的錢包。

聯網的錢包使用父公鑰匯出子公鑰,可選的幫助分發它們,或者監控花費在那些公鑰上的輸出,為花費那些輸出建立未簽名的交易,並傳輸未簽名的交易到只簽名錢包。

通常使用者使用只簽名錢包有機會看到未簽名交易的細節特別是輸出細節。

在可選的檢查步驟之後,只簽名錢包使用父私鑰匯出合適的子私鑰,並簽名那些交易,傳送簽名過的交易給聯網的錢包。

然後聯網的錢包將簽名過的交易廣播到P2P網路上。

以下小節描述了只簽名錢包的兩個最常見的變體:離線錢包和硬體錢包。

離線錢包

一些完整的全服務錢包程式可以作為兩種獨立的錢包執行:一個程式例項作為只簽名錢包(通常稱為離線錢包),另外一個程式例項聯網錢包(通常稱為線上錢包或只觀看錢包)。

離線錢包如此命名,因為它打算在不連線到任何網路的裝置上執行,大大減少了攻擊次數。如果是這種情況,通常由使用者使用可移動介質(如USB驅動器)處理所有資料傳輸。使用者的工作流程如下:
  • (離線)禁用裝置上的所有網路連線,並安裝錢包軟體。在離線模式下啟動錢包軟體,以建立父私鑰和公鑰。將父公鑰複製到可移動媒體。
  • (線上)將錢包軟體安裝在另一臺裝置上,該裝置聯網,並從可移動媒體匯入父公鑰。像使用全服務錢包一樣,分發公鑰來接收付款。當準備花費satoshis時,填寫輸出細節,並將錢包生成的未簽名交易儲存到可移動媒體。
  • (離線)在離線例項中開啟未簽名的事務,檢視輸出詳細資訊,確保他們將正確的金額用於正確的地址。這可以防止線上錢包上的惡意軟體欺騙使用者簽署支付給攻擊者的交易。經過審查,將交易簽名並將其儲存到可移動媒體上。
  • (線上)在線上例項中開啟簽名的交易,以便將其廣播到P2P網路。
離線錢包的主要優點是可以大大提高全服務錢包的安全性。只要離線錢包不會受到損害(或有缺陷),使用者在簽署之前稽核所有外發交易,即使線上錢包被破壞,使用者的satoshis也是安全的。

離線錢包的主要缺點是麻煩。為了安全最大化,它們要求使用者僅將一個裝置專用於離線任務。離線錢包必須在資金花費時啟動,使用者必須將資料在線上裝置和離線裝置中來回複製。

硬體錢包

硬體錢包是專用於執行只簽名錢包的裝置。他們的奉獻使他們能夠消除設計用於一般用途的作業系統中存在的許多漏洞,從而允許他們直接與其他裝置通訊,以便使用者不需要手動傳輸資料。使用者的工作流程如下:

  • (硬體)建立父私鑰和公鑰。將硬體錢包連線到網路裝置,以便它可以獲取父公鑰。
  • (網路)正如使用全服務錢包一樣,分發公鑰來接收付款。當準備花費satoshis時,填寫交易詳情,連線硬體錢包,然後點選支出。聯網的錢包將自動傳送交易詳細資訊到硬體錢包。
  • (硬體)在硬體錢包的螢幕上檢視交易詳情。某些硬體錢包可能會提示輸入密碼或PIN碼。硬體錢包將事務簽名並上傳到聯網的錢包。
  • (網路化)聯網的錢包從硬體錢包接收簽名的交易,並將其廣播到網路。

硬體錢包的主要優點是可以大大提高全服務錢包的安全性,而且不需要比離線錢包更麻煩。

硬體錢包的主要缺點是麻煩。即使麻煩小於離線錢包,使用者仍然必須購買硬體錢包裝置,並在他們需要使用簽名錢包簽名交易時候隨身攜帶。

一個額外的(希望是暫時的)缺點是,在撰寫本文時,流行的錢包程式很少支援硬體錢包 - 雖然幾乎所有流行的錢包程式已宣佈有意外去至少支援一種硬體錢包型號。

僅分發錢包

錢包程式在難以安全的環境中執行(如Web伺服器)可以設計為只分發公鑰(包括P2PKH或P2SH地址 )而沒有更多功能。設計這些極簡錢包有兩種常見的方法:


Distributing-Only Wallets

使用多個公開金鑰或地址預填充資料庫,然後使用其中一個數據庫條目根據請求分發pubkey指令碼或地址。為了避免金鑰重用,Web伺服器應該跟蹤使用的金鑰,永遠不會用完公鑰。通過使用父公鑰,可以使其更容易,如下一個方法中所建議的那樣。

使用父公鑰建立子公鑰。為了避免金鑰重用,必須使用一種方法來確保相同的公鑰不分發兩次。這可以是分配的每個金鑰的資料庫條目或指向金鑰索引號碼的遞增指標。

這兩種方法都不會增加大量的開銷,特別是如果資料庫被用於關聯輸入的付款和一個單獨的公鑰來跟蹤付款的時候。有關詳細資訊,請參閱支付流程部分。

錢包檔案

比特幣錢包核心就是私鑰的集合。這些集合以數字方式儲存在檔案中,甚至可以物理儲存在紙張上。

私鑰格式

私鑰是用於從特定的地址解鎖satoshis。在比特幣中,標準格式的私鑰只是一個256位數字,在值的範圍是:
0x01 and 0xFFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFE BAAE DCE6 AF48 A03B BFD2 5E8C D036 4140, 幾乎覆蓋 2256-1個數值的整個範圍.該範圍由比特幣使用的secp256k1 ECDSA加密標準決定。

錢包匯入格式(WIF)


為了使私鑰的複製不容易出錯,可以使用錢包匯入格式WIF。WIF在私鑰上使用base58Check編碼,大大降低了複製錯誤的機率,這和標準比特幣地址很類似 。

  1. 拿出一個私鑰。
  2. 在mainnet 地址之前新增一個0x80位元組或testnet 地址新增一個0xef位元組。
  3. 如果它與壓縮的公鑰一起使用(在後面的小節中描述),則會新增一個0x01位元組。如果與未壓縮的公鑰一起使用,則不附加任何內容。
  4. 在擴充套件金鑰上執行SHA-256雜湊。
  5. 對SHA-256雜湊的結果執行SHA-256雜湊。
  6. 取第二個SHA-256雜湊的前四個位元組;這是校驗和。
  7. 從第2點的擴充套件金鑰的末尾新增第5點的四個校驗和位元組。
  8. 使用Base58Check編碼將結果從位元組字串轉換為Base58字串。

該過程很容易可逆,使用Base58解碼功能,並刪除填充。

迷你私鑰格式

Mini 私鑰格式是一種用於在30個字元內編碼私鑰的方法,可以將金鑰嵌入在小物理空間中,例如物理比特幣令牌,抗損害的QR碼。

  1. 迷你金鑰的第一個字元是'S'。
  2. 為了確定迷你私鑰是否格式正確,在私鑰中添加了一個問號。
  3. 計算SHA256雜湊。如果產生的第一個位元組為“00”,則格式良好。這個關鍵的限制是錯誤檢測機制。使用者使用隨機數強制窮舉,直到產生格式正確的迷你私鑰。
  4. 為了匯出完整的私鑰,使用者只需要使用原始迷你私鑰的單個SHA256雜湊。這個過程是單向的:從派生金鑰很難計算出迷你私鑰格式。
由於與'l'的視覺相似性,許多實現不允許迷你私鑰中使用字元'1'。

資源:建立和兌現這些金鑰的常用工具是Casascius Bitcoin Address Utility。


公鑰格式

比特幣ECDSA公鑰表示在secp256k1中定義的特定橢圓曲線(EC)上的一個點。在其傳統的未壓縮形式中,公鑰包含一個標識位元組,一個32位元組的X座標和一個32位元組的Y座標。下面非常簡化的圖表顯示了比特幣在連續數字欄位上使用的橢圓曲線y 2 = x 3 + 7上的這一點。

Point On ECDSA Curve

(Secp256k1實際上是一個大素數的modulos座標,儘管原理是相同的,但是它產生非連續整數的欄位和明顯不太清楚的圖。

通過去除Y座標,可以實現公鑰大小几乎減少50%,而不會改變任何基本法則。這是可能的,因為沿曲線上只有兩個點共享任何特定的X座標,因此32位Y座標可以用一個位來代替,表示點在圖示中出現的是頂部還是底部。

建立這些壓縮的公鑰不會丟失任何資料 - 只需要少量的CPU來重建Y座標和訪問未壓縮的公鑰。官方的secp256k1文件中描述了未壓縮和壓縮的公鑰,並在廣泛使用的OpenSSL庫中預設支援。

因為他們易於使用,並減少了儲存每次花費輸出的將近一半的鏈空間,壓縮公鑰是Bitcoin Core預設的,並是所有比特幣軟體推薦使用的。

然而,Bitcoin Core在0.6之前使用未壓縮的金鑰。這造成一些麻煩,因為非壓縮金鑰的雜湊格式不同於壓縮金鑰的雜湊格式,所以同一個金鑰要有兩個不同的P2PKH地址。這也意味著金鑰必須在簽名指令碼中以正確的格式提交,能匹配上一個先前輸出的 pubkey指令碼中的雜湊。

因此,Bitcoin Core使用幾個不同的識別符號位元組來幫助程式識別如何使用金鑰:
  • 私鑰意味著在被Base-58編碼前使用添加了0x01的壓縮公鑰。(參見上面的私鑰編碼部分。)
  • 未壓縮的公鑰以0x04開頭; 壓縮的公開金鑰以0x03或0x02開頭,取決於它們是否大於或小於曲線的中點。這些字首位元組都在官方的secp256k1文件中定義使用。

分層確定性金鑰建立


分層確定性金鑰建立和傳輸協議(HD協議)極大地簡化了錢包的備份,無需相同錢包的多個程式之間重複通訊,允許建立可以獨立運作的子帳戶,即使子帳戶遭到入侵,也能使每個父賬戶監控或控制其子帳戶,並將每個賬戶劃分為完全訪問和受限訪問部分,可以允許不受信任的使用者或程式接收或監視付款但不能花費它們。

HD協議利用ECDSA公鑰建立函式point(),其佔用一個大整數(私鑰),並將其轉換為圖形點(即公鑰):


以point()的工作方式,可以通過將現有的父公鑰和另外一個從整數值(i)建立的公鑰組合起來建立子公鑰。這個子公鑰與初始父私鑰加上i值除去所有Bitcoin軟體使用的全域性常量p的產生的餘數經過point()函式而建立的公鑰相同。

這意味著兩個或多個獨立的程式使用一致的整數序列可以從單個父鍵對建立一系列唯一的子鍵,而無需任何進一步的通訊。此外,分發新公鑰的程式可以不得到私鑰而接收付款,這樣允許公鑰分發器工作在一個可能不安全的平臺比如公共Web伺服器。

子公鑰還能通過重複子鍵推導操作來建立他們自己的子公鑰(孫子公鑰)。


無論是建立子公鑰還是進一步派生的公鑰,可預測的整數序列將不如對所有交易使用單個公鑰,因為知道一個子公鑰的任何人可以找到從相同的父公鑰建立的所有其他子公鑰。相反,可以使用隨機種子來確定性地生成整數序列,使得子公鑰之間的關係對於沒有該種子的任何人都是不可見的。

HD協議使用單個根種子建立具有不可推導的整數值的子,孫和其他派生金鑰。每個子金鑰還可以從它的父金鑰獲取一個確定性產生的種子,稱為鏈程式碼,所以一個鏈程式碼的損害不會影響整個層次的整數序列,允許主鏈程式碼即使例如基於Web的公鑰分發器被hacked還能繼續使用。


Overview Of Hierarchical Deterministic Key Derivation
  • 如上所述,HD金鑰匯出需要四個輸入:
  • 父私鑰和父公鑰是常規的未壓縮256位ECDSA金鑰。
  • 父鏈程式碼是256位的看似隨機資料。
  • 索引號是程式指定的32位整數。
在上述圖示中所示的正常格式中,將父鏈程式碼,父公鑰和索引號饋送到單向加密雜湊(HMAC-SHA512),以產生512位的確定性生成但似乎隨機的資料。雜湊輸出右側的看似隨機的256位用作新的子鏈程式碼。使用雜湊輸出的左側的看似隨機的256位作為與父私鑰或父公共金鑰組合的整數值分別建立子私鑰或子公鑰:

指定不同的索引號將從相同的父鍵建立不同的不可連結的子鍵。使用子鏈程式碼重複子鍵的過程將建立不可連結的孫子鍵。

因為建立子鍵需要鍵和鏈程式碼,所以鍵和鏈程式碼一起被稱為擴充套件鍵。擴充套件私鑰及其相應的擴充套件公鑰具有相同的鏈碼。(頂級父級)主金鑰和主鏈碼從隨機資料派生,如下所示。


Creating A Root Extended Key Pair

根據128位,256位或512位隨機資料​​建立一個根種子。這個少於128位的根種子是使用者需要備份的唯一資料,以便使用特定設定來匯出由特定錢包程式建立的每個金鑰。

Warning icon 警告:在撰寫本文時,HD錢包程式預計不會完全相容,因此使用者只能使用相同的HD錢包程式,具有與特定根種子相同的HD相關設定。

根種子被雜湊以建立512位的看似隨機資料,從其中建立主專用金鑰和主鏈碼 ,主擴充套件私鑰)。使用point()從主金鑰派生主公鑰,其中連同主鏈碼,是主擴充套件公鑰。主擴充套件鍵在功能上等同於其他擴充套件鍵;只有他們的位置在層次結構的頂部,使他們特別。


固化金鑰

固化擴充套件金鑰修復了普通擴充套件金鑰的潛在問題。如果攻擊者獲得正常的父連結程式碼和父公開金鑰,他可以強制所有從其派生的鏈碼。如果攻擊者也獲得了一個子孫,孫子或進一步的私鑰,他可以使用鏈碼生成所有擴充套件的私鑰從私鑰下降,如下圖所示的孫子孫子孫代。


Cross-Generational Key Compromise

也許更糟的是,攻擊者可以反轉正常的子私鑰匯出公式,並從子私鑰中減去父鏈程式碼來恢復父私鑰,如上圖所示的子代和父代。這意味著從其中獲取擴充套件的公鑰和任何私鑰的攻擊者可以恢復公鑰的 私鑰和所有鍵從它下降。

因此,擴充套件公開金鑰的鏈碼應該比標準的公鑰更好地保護,並且應該建議使用者不要匯出甚至非擴充套件的私鑰到可能不可信的環境。

這可以通過用強化的金鑰匯出公式替換正常金鑰推導公式來進行一些折衷。

上述部分中描述的正常金鑰匯出公式將索引號父鏈程式碼和父公開組合在一起,以建立子鏈程式碼和與父私鑰組合的整數值來建立子私鑰。


Creating Child Public Keys From An Extended Private Key

如上所示的硬化公式將索引號父鏈程式碼和父私鑰組合在一起,以建立用於生成子鏈的資料程式碼和子私鑰。該公式使得無法建立子公共金鑰,而不知道父私鑰。換句話說,父擴充套件公鑰不能建立硬化的子公共金鑰。

因此,固化擴充套件私鑰比正常的擴充套件私鑰或更無用,但是固化擴充套件私鑰在多層金鑰匯出之間建立防火牆保證損害不能發生。因為硬化子擴充套件公鑰不能自己生成孫子鏈碼,父擴充套件公鑰的妥協不能與妥協的孫子私鑰來建立孫孫擴充套件私鑰。

HD協議使用不同的索引號來指示是否應生成正常或硬化的金鑰。索引號從0x00到0x7fffffff(0到2 31 -1)將產生正常的鍵;索引號從0x80000000到0xffffffff將生成一個硬化金鑰。為了簡化說明,許多開發人員使用素數符號表示硬化金鑰,因此第一個正常金鑰(0x00)為0,第一個硬化金鑰(0x80000000)為0。

(Bitcoin開發人員通常使用ASCII撇號而不是unicode主要符號,這是我們以後的慣例)

該緊湊描述進一步與由m或M字首的斜槓組合,以指示層次結構和鍵型別,其中m是私鑰和M是公鑰。例如,m / 0'/ 0/122'是指主私鑰的第一個硬化子節點(按索引)的第一個正常子節點(按索引)的第123個硬化私有子節點(按索引號)。以下層次結構說明了主要符號和強化的關鍵防火牆。


Example HD Wallet Tree Using Prime Notation

遵循BIP32 HD協議的錢包只能建立主私鑰m的固化子金鑰,以防止受損的子鍵損害主鍵。由於主金鑰沒有正常的孩子,所以在HD錢包中不使用主公鑰。所有其他鍵可以有普通的孩子,所以可以使用相應的擴充套件的公鑰。

HD協議還描述了擴充套件公鑰和擴充套件私鑰的序列化格式。有關詳細資訊,請參閱開發者參考的錢包部分或BIP32中完整的HD協議規範。

儲存根種子

HD協議中的根種子是必須精確備份的128,256或512位隨機資料。為了更方便地使用諸如儲存或手工複製的非數字備份方法,BIP39定義了一種從偽偽建立512位根種子的方法普通自然語言詞彙(助記符)本身是由128到256位的熵建立的,並且可選地受密碼保護。

所產生的詞數與所用熵的數量有關:

密碼短語可以是任何長度的。它只是附加到助記符偽句子,然後使用HMAC-SHA512將助記符和密碼兩次雜湊2,048次,從而產生一個看似隨機的512位種子。因為雜湊函式的任何輸入都會建立一個看似隨機的512位種子,所以沒有一個基本的方式來證明使用者輸入正確的密碼,可能允許使用者保護種子即使在脅迫下。

有關實現細節,請參閱BIP39。


鬆散金鑰錢包

也稱為“Just a Bunch Of Keys(JBOK)”的Loose-Key錢包是來自Bitcoin Core客戶端錢包的一種形式 。比特幣核心客戶端錢包將通過偽隨機數生成器(PRNG)自動建立100 私鑰 / 公鑰,以供以後使用。

這些未使用的私鑰儲存在虛擬“金鑰池”中,每當使用以前生成的金鑰時,將生成新金鑰,確保池保持100個未使用的金鑰。(如果錢包被加密,則僅在錢包被解鎖時才產生新金鑰。)

考慮到備份必須手動執行以儲存新生成的私鑰,這在備份金鑰方面造成了相當大的困難。如果在備份之前生成,使用,然後丟失新的金鑰對集,則儲存的satoshis可能永遠丟失。許多舊式移動錢包遵循類似的格式,但只有根據使用者需求生成新的私鑰。

由於備份麻煩,此錢包型別正在被積極淘汰,不鼓勵使用。