作者:SJORS PROVOOST
來源:https://sprovoost.nl/2022/11/10/what-is-a-bitcoin-address/
比特幣地址不是比特幣區塊鏈的一部分,而是一種記號,比特幣(錢包)軟件用它來溝通將比特幣發送至何處:要么是發給某個公鑰(P2PK)、要么是某個公鑰的哈希值(P2PKH)、腳本的哈希值(P2SH)、隔離見證公鑰的哈希(P2WPKH),或者是隔離見證腳本的哈希(P2WSH)。地址中還包含一些關於其自身所屬類型的元數據。
(譯者註:比特幣的地址類型會隨著比特幣網絡的升級和腳本編寫方式的標準化而不斷增加。其實上面提到的地址類型,除了P2PK 和P2PKH 是隨著比特幣的初次發布就出現的,其餘都是後來才出現的。現在,由於Taproot 升級在2021 年激活,比特幣又多了一種地址類型“P2TR”。)
比特幣地址使用獨有的編號系統來表達上述付款方式。本文將分別介紹這些互不相同的編號系統,並深入分析一般的比特幣地址和bech32 地址具備哪些優點。此外,我們還將解釋第一版bech32 地址所包含的那個(威脅程度較低的)漏洞從何而來,又是如何解決的。最後,我們談到了量子計算的影響。
- 本文節選自我的新書《比特幣:未竟的研究》 -
歷史背景
當你向某人發送比特幣時,你實際上是在創建一筆包含多個輸入和至少一個輸出的交易。輸出通過內置的約束(在法律上叫作“負擔”(encumbrance),即,限制資產所有權的轉讓)指定可以花費該輸出的人。
最微不足道的負擔是允許任意人花費這筆比特幣。這不是個好主意,因為這一筆比特幣將很快被人偷走。因此,在比特幣發展初期,區塊鏈上的絕大多數比特幣只能使用兩種約束:Pay-to-Public-Key (P2PK) 或Pay-to-Public-Key-Hash (P2PKH)。前者可以理解為“只有持有公鑰X 對應私鑰的人才能花費該比特幣”,後者可以理解為“只有持有(哈希值為X 的)(秘密)公鑰對應私鑰的人才能花費該比特幣”。
彼時,我們還可以將比特幣發送到收款方的IP 地址,但是這個功能已經在2012 年停用了。其使用方法是,你需要連接到收款方的IP 地址,向收款方請求公鑰,收款方會將公鑰給你1 。然後,你的錢包會創建帶有P2PK 腳本的比特幣。
這個工作流程放到今天來看會有些奇怪2 ,但是符合彼時Napster 和Kazaa 等點對點應用的常見模式,即,直接與其他人建立連接,從他們那裡下載東西。現如今,你極有可能不知道你的朋友的IP 地址,如果他們正在使用移動設備,他們的IP 地址甚至會一直發生變化。雖然你可以指示你的比特幣節點專門連接到你的朋友的節點,但是在通常情況下只會連接到隨機節點,見第二章。
更常見的交易方式與銀行轉賬類似。收款方給你一個地址,你將比特幣發送到這個地址上,就像你把錢發送到銀行賬戶上。最初,我們使用的都是P2PKH 作為地址的(P2PKH 的含義見下文的解釋)。
通過這種方式,交易不是直接發送給收款方,而是經由網絡中的所有節點進行廣播,最終被挖礦節點發現並打包到區塊中。你的對手方的節點有可能從對等節點那裡看到該交易,或收到該交易所在區塊。
第三種交易方式是挖礦,將挖礦得到的區塊獎勵發送給自己。最初,比特幣軟件中內置了挖礦軟件。因此,只要你下載了比特幣軟件,你的比特幣軟件就會開始挖礦,然後將比特幣發送到你的錢包,這種情況下無需交流地址。這些比特幣都以P2PK 為約束3 。
地址是什麼?
地址是用來表示哪個腳本需要進入區塊鏈的便捷方式。正如我們在上文所述,腳本的目的是在比特幣上施加限制,只允許收款方花費它們4 。地址本身並不存在於區塊鏈上,地址甚至不包含完整的腳本。
就以往使用最多的兩類腳本而言,地址僅用於Pay-to-Public-Key-Hash (P2PKH)。當錢包看見這種地址時,會生成一個腳本,要求花費其中的比特幣的人必須持有該哈希值的對應公鑰( 第10 章提供了真正的腳本)。只有哈希值會被公開,公鑰會一直保密,直到收款方花費這筆比特幣為止。
P2PKH 地址以數字1 開頭,後面跟著的是公鑰的哈希值。地址使用base58 編碼,如下例所示:
1HLoFgMiDL3hvACAfbkDUjcP9r9veUcqAF
Base 系統是什麼
要想理解base58,我們首先要了解base 系統的基本原理。
以base10 為例,把它想像成你的10 個手指。因此,如果你想要表達數字115(1、1、5),你可以用雙手做出與1、1、5 對應的三個手勢。自從人類發明了泥板和紙,你也可以用筆寫下這些數字,比起使用手指要方便得多。因此,base10 是使用10 個不同符號的十進制系統,利用這10 個符號的各種組合來表達任意數字(整數)。
除此之外,還有很多不同的base 系統。例如,古巴比倫人使用的是base60。為了閱讀機器代碼,我們通常會使用十六進制,也就是base16 —— 使用數字0 到9、字母A 到F 這16 個字符。與此同時,計算機內部則傾向於使用base2(一種二進制數字系統),因為晶體管只有開啟和關閉兩種狀態。這意味著只需要兩個數字0 和1 就可以完成所有操作,你可以用它們來表示任何數字。
中本聰引入了base58系統,使用58 個不同的符號:數字0 到9 以及字母表中絕大部分小寫和大寫字母。但是,一些用戶容易混淆和認錯的字母和數字不包含在內—— 例如,數字0 和大寫字母O,大寫字母I 和小寫字母l。
你見過電子郵件附件的源代碼嗎?一大串奇怪的數字。這就是base64,而base58 正是在bas64 的基礎上誕生的。但是,base64 包含下劃線、加號、等號和斜杠之類的字符。 base58 去掉了這些字符,讓肉眼檢查變得更加容易,而且可以有效應用於URL。
Base58 和Pay-to-Public-Key-Hash
這與P2PKH 有什麼關係? P2PKH 地址以1 開頭,緊跟著的是用base58 編碼的公鑰哈希值。
這就是當你想要從其他人那裡接收比特幣時,你需要發送給他們的信息。你也可以只發送0x00 5和公鑰給他們。或許他們能夠成功將0x00 翻譯出來,但是大概率不行。
從理論上來說,你可以向其他人發送用十六進制(區塊鏈上使用的格式)表示的比特幣腳本,因為比特幣腳本是二進制信息。在區塊鏈上,這樣的比特幣腳本的大意是:“如果這個人擁有正確的公鑰哈希和與之對應的公鑰,你就可以花費這筆比特幣。”如果你想更深入了解比特幣腳本是如何運作的,請參見第十章。
雖然有如此多表示方式可以選擇,但是人們選擇的通常是標準化的地址格式。這就解釋了為什麼所有傳統比特幣地址都以1 開頭,而且長度也都差不多。
除了用來發送比特幣地址,base58 也可以用來傳遞私鑰。在這種情況下,為首的符號是5(代表128,作為版本字節),後面跟著私鑰。
過去,用戶用的是可以打印出來的紙錢包。如果它們是在沒有後門的情況下安全生成的,那麼這張紙的一面是以“1”開頭的字符串,另一面是以“5”開頭的字符串,並註明只可以出示比特幣地址,不應共享私鑰。
也有以“3”開頭的地址,表示把比特幣鎖在腳本哈希值裡面,而非公鑰哈希值內。我們將在第十章介紹Pay to Script Hash (P2SH)。這類地址通常是多簽地址,不過也有可能是SegWit 地址6 。
雖然base58 地址表現良好,但還是有改進空間。於是,我們有了bech32。
bech32 登場
2017 年3 月,Pieter Wuille 談到了一種新的地址格式bech32。自SegWit 成功激活以來,bech32 就一直被應用至今。顧名思義,bech32 是一種base32 系統。也就是說,你可以使用幾乎所有字母和所有數字,除去少數容易產生混淆的數字和字母。
講解視頻: https://youtu.be/NqiN9VFE4CU
bech32 和base58 之間最大的區別是不混用大小寫字母。每個字母只會出現一次(要么全部大寫,要么全部小寫),因此讀出口會容易得多。每個字母或數字與其對應值之間的精準映射是固定的,但是相當隨意:Q 就表示0,P 就表示1,背後沒有深層含義。
- bech32 映射表。例如,q 表示0,3 表示17(1+16) -
bech32 7地址由使用“1”分隔的兩部分組成,例如, bc1q9kdcd08adkhg35r4g6nwu8ae4nkmsgp9vy00gf 。
前半部分是有意讓人讀懂的,例如,“bc”(代表Bitcoin)或“Inbc”(比特幣上的閃電網絡)。 “b”和“c”等字母代表的值沒有任何含義。它們的存在只是為了讓人識別:“懂了,如果地址以'bc' 開頭,指的就是作為密碼學貨幣的比特幣。”但是,錢包會查看這些值是否存在,作為可信度檢查,而且這些值也包含在校驗和內。
“1”只是分隔字符,不代表任何值。如果你查看bech32 的映射表,你會發現“1”不包含在內,意思是“跳過它”。
後半部分以SegWit 版本號為開頭。版本0 由Q(bc1q…)代表(見第三章)。版本1 就是我們所說的Taproot(見本書的第4 部分),用“P”(bc1p…)表示。如果是版本0 SegWit,版本號後面會跟著20 或32 個字節,分別表示公鑰哈希值或腳本哈希值。二者的長度不同,是因為SegWit 使用的是腳本的SHA256 哈希(32 個字節), 而非腳本的RIPEMD160 哈希(20 個字節)。
在base58 中,腳本哈希值與公鑰哈希值長度相同。但是在SegWit 中,二者的長度不同。因此,只要查看地址的長度,你立馬就可以知道你是在向腳本還是公鑰哈希付款。順帶一提,Taproot 消除了這種長度差異,進一步提高了隱私性。
因此,bench32 的特徵是地址的後半部分只使用32 種字符,除此之外與base58 區別不大。看到這個特徵,你就會明白:“啊哈,這是一個P2PKH 地址。”在這種情況下,Pay-to-Witness-Public-Key-Hash (P2WPKH),其中“Witness”指的是SegWit ,但核心思想沒有變:讓人和電腦能夠根據一個簡短的前綴識別地址的類型,前綴後面跟著公鑰或腳本的哈希值。
32 維擲鏢遊戲
但是,簡潔性並非唯一的優點。還有一個優點是糾錯性,至少是檢錯性。
如果你輸錯了地址,最壞的結果是你將比特幣發送到了錯誤的公鑰哈希值上。當接收方試圖花費比特幣時,會發現自己的公鑰的哈希值並不符合區塊鏈的要求,因為發送方之前輸錯了地址。這筆比特幣就再也找不回來了。
幸好base58 地址在末端有一個校驗和。如此一來,如果你輸錯了地址,地址末端的校驗和驗證就會失敗。你的錢包會向你發出告警,並拒絕發送該交易(區塊鏈不會保護你,好在你的錢包會)。但是,如果你實在不走運,在輸錯的情況下也有可能恰好得到正確的校驗和。
Bech32 就是為了避免出現如此極端的巧合而設計的。另外,Bech32 不僅會告訴你輸錯了,還會告訴你錯在何處。具體方法是取地址的所有字節,使用某種複雜的數學公式對其進行哈希運算。即使你輸錯了4 處,Bech32 依然可以知道哪裡輸錯了,以及實際的值是什麼。如果你輸錯了不止4 處,Bech32 就沒辦法了。
我們通過一則比喻進行說明:你在一堵牆上畫了一堆沒有重疊的圓圈。每個圓圈的靶心代表一個正確的值,而圓圈內其它點各代表一個輸入錯誤。如果你是個技術高超的飛鏢玩家,大多數時候你會命中靶心,這意味著你輸對了值。如果你稍稍錯過靶心,但是依然落在圓圈內,這意味著你輸入的值稍微有點不對。檢錯就是知道你錯過了靶心。糾錯就是將飛鏢移動到最近的靶心。
這裡面的思路是,你想要盡可能擴大圓圈來方便最粗枝大葉的飛鏢玩家,卻又不想浪費太多空間。同樣地,我們不想將比特幣地址變成數百個字符那麼長。這是數學家最喜歡的優化問題。
不同於二維牆,bech32 好比是一面帶有32 維超球面的32 維牆。當你輸入地址時,在這個32 維空間內的某處產生了輕微偏差,但無論看起來如何,你仍位於這個超球面內。在這種情況下,你的錢包知道錯在哪裡,可以有效防止因發錯地址而造成比特幣丟失的問題8 。
bech32 的漏洞
在2019 年,人們發現如果一個bech32 地址的最後一個字符是P,你又意外在後面多輸入了一個或幾個Q,依然能通過校驗和驗證,你也不會收到輸入錯誤的提示。你的錢包軟件會認為地址輸入正確,任由你將比特幣發送至錯誤地址,導致這筆比特幣無法被花費,就像我們在上文解釋的那樣。
好消息是,bech32 只用於SegWit,而SegWit 地址有長度限制—— 只能是20 個字節或32 個字節。幸運的是,如果你在長度為20 或32 字節的地址後面多輸了一個Q,你輸入的地址就會因為超出長度限製而無效。你的錢包會發現這個問題,並拒絕發送比特幣。人們原本考慮針對Taproot 引入類似的地址長度限制,但是下文提到的解決方案免去了這一需求。靈活的地址長度更有助於我們未來改進Taproot。
bech32m 誕生
為了修復bech32 的漏洞,有人提議了一個叫作bech32m 的新標準9 。 bech32m 實際上是非常簡單的改變:在bech32 校驗和公式中額外添加了一個數字,以確保任何新增字符都會生成無效校驗和。
這個新標準只應用於Taproot 地址和未來地址。對於SegWit 地址來說,一切都沒有改變,因為它們已經有了20 或32 字節長度限制的保護。在筆者撰文期間,大多數錢包軟件都支持新的bech32m 標準。
是什麼讓我擺脫焦慮,愛上量子計算?
順帶一提,Pay-to-Public-Key-Hash (P2PKH) 被認為更能抵禦量子攻擊,因為你無需透露自己的公鑰。缺點是哈希佔用的空間更大—— 但這在當時不成問題,因為區塊遠不到填滿的程度。
很多人擔心量子計算機最終會破壞比特幣密碼學的安全性,讓妄圖竊取比特幣的量子黑客有可趁之機。如果他們成功竊取數百萬個比特幣,甚至會導致市場崩盤。
問題是,儘管P2PKH 被廣泛採用,其中有500 萬至1000 萬比特幣的公鑰已經公開。具有諷刺意味的是,鑑於如此多比特幣容易遭到量子黑客竊取,試圖以此保護剩餘的比特幣已經意義不大。即使你的比特幣因為使用了P2PKH 二不會被盜,也免不了因價格崩盤而變得一文不值。
物理學家S tepan Snigirev和數學家Andrew Poelstra在兩集題為《比特幣做了什麼》的播客中解釋了量子攻擊在短期內造成毀滅性後果的可能性以及可行的應對措施。
區塊空間現在變得非常稀缺,因此無需將公鑰哈希存儲在寶貴的區塊空間內可以幫助用戶節省費用。這就是為什麼在新的Taproot 軟分叉中(見本書的第四部分),比特幣地址又成了P2PK 10 。請注意使用Taproot 地址不具有強制性,因此如果你不認同上述推理,你可以選擇不使用Taproot。
腳註
1.為滿足代碼“考古”愛好者的好奇心:發送方節點會有一個UI 對話框提示轉賬數額和IP 地址。函數StartTransfer() 創建一個空白支票交易,接收方節點上的checkorder 會在其中插入一個P2PK 腳本(作為scriptPubKey)。隨後,OnReply2() 會插入數額,簽署交易,將交易返回給接收者並進行廣播。 源代碼。 ↩
3.為什麼中本聰最初發布的版本同時支持P2PK 和P2PKH?我們並不確定具體原因。 P2PK 的支付方式實際上只用來向IP 地址付款,以及向礦工支付區塊獎勵。二者都不需要人際交互。在涉及人際交互的場景中,用戶使用的是P2PKH。使用地址指的是P2PKH 而非P2PK 。自動化系統不需要地址的概念,因為它們也可以處理腳本,也就不需要像P2PK 地址這樣的概念。 ↩
4.到目前為止,腳本都與銀行賬戶類似。我們將在第十章了解到腳本的功能遠不只是為所有者保管資金而已。 ↩
5.一對以0x 為前綴的十六進制數字通常被用來表示一個字節,它可以表示16 × 16 = 256 個不同值。因此,0x00 表示一個字節且它的值為0。 ↩
6.正如第三章解釋的那樣,SegWit 通常使用bech32 地址。但是,過了很長時間,我們才等到所有錢包和交易所都支持向bech32 地址付款。為了繼續利用SegWit 的一些優點,我們引入了一種在發送方角度看起來像是普通P2SH 的地址類型,但是背後包含了SegWit 的魔力。這類地址叫作P2SH-P2WPKH 地址。 ↩
7. bech32 由BIP173 提出。 ↩
8.早期的以太坊錢包不使用錯誤檢測,因為它們的地址標準缺少校驗和。雖然EIP55 在2016 年引入了校驗和,但不是所有錢包都執行錯誤檢測。即使到了2017 年末,仍有人因輸錯地址而丟失以太幣。 ↩
9. bech32m 由BIP 350 提出。 ↩
10.具體原因見BIP 341 的註釋。 ↩