【比特幣】 BIP-0037 詳細說明
BIP-0037 詳細說明
BIP: 37
Layer: Peer Services
Title: 連線布隆過濾器
Author: Mike Hearn <[email protected].com>
Matt Corallo <[email protected].me>
Comments-Summary: No comments yet.
Comments-URI: https://github.com/bitcoin/bips/wiki/Comments:BIP-0037
Status: Final
Type: Standards Track
Created: 2012 -10-24
License: PD
概述
這個BIP為P2P協議增加了新的支援,允許同行減少他們傳送的交易資料量。 在版本握手完成之後,節點可以選擇在每個連線上設定過濾器。過濾器被定義為從交易派生的資料的布隆過濾器。 布隆過濾器是一個概率資料結構,允許測試整合員 - 他們可以有誤報,但不是假的否定。
本文不會詳細介紹布隆過濾器如何工作,讀者可以參考維基百科進行介紹。
動機
隨著比特幣使用量的增長,下載資料塊和交易廣播所需的頻寬量也隨之增加。 實施簡化支付驗證的客戶不會試圖完全驗證區塊鏈,而只是檢查區塊標題連線在一起是正確的,並且相信高難度鏈中的交易實際上是有效的。 有關此模式的更多詳細資訊,請參閱比特幣紙張。
今天,SPV客戶端必須下載塊和全部廣播交易的全部內容,才能拋棄絕大多數與錢包無關的交易。 這減慢了他們的同步過程,浪費了使用者頻寬(在手機上經常被測量)並且增加了記憶體使用。 所有這三個問題都觸發了真正的使用者投訴Android“比特幣錢包”應用程式,實施SPV模式。 為了使鏈同步更快,更便宜,並且能夠在記憶體有限的舊電話上執行,我們希望遠端對等方在通過網路傳送不相關的事務之前丟棄它們。
設計原理
實現既定目標的最明顯的方法是讓客戶端將金鑰列表上傳到遠端節點。 出於以下原因,我們採取更復雜的方法:
- 隱私:由於Bloom過濾器是概率性的,客戶端選擇誤報率,所以節點可以在精度和頻寬使用之間進行權衡。 一個能夠獲得大量頻寬的節點可能會選擇高FP率,這意味著遠端節點無法準確地知道哪些事務屬於客戶端,哪些不屬於客戶端。 一個頻寬非常小的節點可能會選擇使用非常準確的過濾器,這意味著它們只能獲得與其錢包實際相關的已傳送事務,而遠端對等可能能夠將事務與IP地址(以及相互之間)相關聯。
- 布隆過濾器是緊湊的,測試它們的成員資格是快速的。 這樣就可以以最小的風險開啟DoS攻擊的潛力,從而達到令人滿意的效能特點。
規格
新訊息
我們開始在協議中新增三條新訊息:
filterload
其中設定當前布隆過濾器在連線上filteradd
它將給定的資料元素新增到連線當前過濾器,而不需要設定全新的資料元素filterclear
這會刪除當前的過濾器並返回到BIP37的常規使用。
請注意,不存在filterremove命令,因為從本質上來說,Bloom過濾器是僅附加資料結構。 一旦添加了一個元素,就不能從頭開始重建整個結構。
filterload
命令定義如下:
欄位大小 | 描述 | 資料型別 | 註釋 |
---|---|---|---|
? | filter | uint8_t[] | 過濾器本身只是一個任意位元組對齊大小的位欄位。 最大大小是36,000位元組。 |
4 | nHashFuncs | uint32_t | 在此過濾器中使用的雜湊函式的數量。 該欄位中允許的最大值是50。 |
4 | nTweak | uint32_t | 將隨機值新增到布隆過濾器所使用的雜湊函式中的種子值。 |
1 | nFlags | uint8_t | 一組控制如何將匹配項新增到過濾器的標誌。 |
請參閱下面有關Bloom過濾器演算法的說明,以及如何選擇nHashFuncs和過濾器大小以獲得所需的誤報率。
在接收到一個filterload
命令後,遠端節點將立即將其宣告的廣播事務(在inv包中)限制為匹配過濾器的事務,其中匹配演算法在下面指定。 這些標誌控制匹配演算法的更新行為。
filteradd
命令定義如下:
欄位大小 | 描述 | 資料型別 | 註釋 |
---|---|---|---|
? | data | uint8_t[] | 要新增到當前過濾器的資料元素。 |
資料欄位的大小必須小於或等於520位元組(任何可能匹配的物件的最大大小)。
給定的資料元素將被新增到布隆過濾器。 必須先使用filterload
提供過濾器。 這個命令是有用的,如果一個新的金鑰或指令碼被新增到一個客戶端錢包,同時它的網路連線開啟,它避免了重新計算和傳送一個全新的過濾器到每個節點的需要(雖然這樣做通常建議 保持匿名)。
filterclear
命令完全沒有引數。
在設定過濾器後,節點不僅停止通告不匹配的交易,還可以為過濾的塊提供服務。 一個過濾的塊由merkleblock訊息定義,並且定義如下:
欄位大小 | 描述 | 資料型別 | 註釋 |
---|---|---|---|
4 | version | uint32_t | 塊的版本資訊,根據軟體版本建立此塊 |
32 | prev_block | char[32] | 先前塊的特定塊的引用的雜湊值 |
32 | merkle_root | char[32] | 對Merkle樹集合的引用,它是與此塊相關的所有事務的雜湊 |
4 | timestamp | uint32_t | 建立此塊時的時間戳記錄(限於2106!) |
4 | bits | uint32_t | 計算的難度目標被用於這個塊 |
4 | nonce | uint32_t | 用於生成此塊…隨機數,以允許頭的變化,並計算不同的雜湊 |
4 | total_transactions | uint32_t | 塊中的交易數量(包括不匹配的) |
? | hashes | uint256[] | 雜湊深度優先(包括標準的varint大小字首) |
? | flags | byte[] | 標誌位,每8個位元組打包,最低有效位先(包括標準的varint大小字首) |
請參閱下面的部分merkle樹雜湊和標誌的格式。
因此,merkleblock訊息是一個塊頭,加上一個merkle樹的一部分,可以用來提取匹配過濾器的事務的標識資訊,並證明匹配的事務資料確實出現在解決的塊中。 客戶可以使用這些資料來確保遠端節點沒有給他們提供從未出現在實際塊中的假交易,雖然說謊通過遺漏仍然是可能的。
擴充套件到現有訊息
版本命令擴充套件了一個新的欄位:
欄位大小 | 描述 | 資料型別 | 註釋 |
---|---|---|---|
1 byte | fRelay | bool | 如果為false,則在收到過濾器{load,add,clear}命令之前,不會公佈廣播事務。 如果丟失或者為真,協議行為不會發生變化。 |
希望使用Bloom過濾的SPV客戶端通常會在版本訊息中將fRelay設定為false,然後根據自己的錢包(或其子集,如果它們重疊不同的對等方)設定過濾器。 在設定過濾器之前,能夠選擇退出inv訊息可以防止客戶端在完成版本握手和設定過濾器之間的短暫時間視窗中充斥流量。
getdata
命令被擴充套件為允許inv
子訊息中的新型別。型別欄位現在可以是MSG_FILTERED_BLOCK(== 3)
而不是MSG_BLOCK
。如果連線上沒有設定過濾器,則忽略過濾塊的請求。 如果已設定過濾器,則會為請求的塊雜湊返回一個merkleblock
訊息。此外,由於merkleblock訊息僅包含交易雜湊列表,因此匹配過濾器的交易也應在傳送merkleblock後的單獨tx訊息中傳送。 這避免了本來需要的慢速往返(接收雜湊,沒有看到一些這些事務,請求)。 請注意,由於目前沒有辦法請求節點中已經在塊中的事務(除了請求完整塊)外,請求節點還沒有用inv
傳送或通告的一組匹配事務必須是 傳送並且任何與該過濾器相匹配的附加交易也可以被髮送。 這允許客戶端(例如參考客戶端)限制其必須記住給定節點的inv
數量,同時仍然向節點提供所需的所有事務。
過濾器匹配演算法
篩選器可以針對任意資料進行測試,以檢視客戶端是否插入了該資料。 因此,出現了什麼樣的資料應該被插入/測試的問題。
要確定事務是否與過濾器匹配,使用以下演算法。 一旦找到匹配,演算法就會中止。
- 測試交易本身的雜湊。
- 對於每個輸出,測試輸出指令碼的每個資料元素。 這意味著輸出指令碼中的每個雜湊和鍵都是獨立測試的。 重要提示:如果在測試交易時輸出匹配,則節點可能需要通過插入序列化的COutPoint結構來更新過濾器。 請參閱下面的更多細節。
- 對於每個輸入,測試序列化的COutPoint結構。
- 對於每個輸入,測試輸入指令碼的每個資料元素(注意:輸入指令碼只包含資料元素)。
- 否則沒有匹配。
通過這種方式,地址,鍵和指令碼雜湊(對於P2SH輸出)都可以被新增到過濾器中。 您還可以匹配在輸入或輸出中標記有眾所周知的資料元素的事務類,例如,實現各種形式的智慧屬性。
為了確保你可以在你的錢包中找到交易消費的輸出,即使你對他們的形式一無所知。 正如你所看到的,一旦設定了連線,過濾器就不是靜態的,並且可以在整個連線生命週期中改變。 這樣做是為了避免以下競爭條件:
- 客戶端設定一個與錢包中的金鑰匹配的過濾器。 然後他們開始下載塊鏈。 使用getblocks來請求客戶端丟失的鏈的部分。
- 服務節點從磁碟讀取第一個塊。 它包含傳送錢給客戶端金鑰的TX 1。 它匹配過濾器,因此被髮送到客戶端。
- 服務對端從磁碟讀取第二個資料塊。 它包含傳送TX 1的TX 2,但TX 2不包含任何客戶金鑰,因此不傳送。 客戶不知道他們收到的錢已經用完了。
通過在步驟2中利用所發現的端點原子地更新布隆過濾器,在步驟3中過濾器將與TX 2匹配,並且儘管在處理第一和第二塊的節點之間沒有暫停,客戶端將會了解所有相關交易。
過濾器的nFlags欄位控制節點的精確更新行為,並且是一個位欄位。
BLOOM_UPDATE_NONE(0)
表示找到匹配項時不調整過濾器。BLOOM_UPDATE_ALL(1)
表示如果過濾器匹配scriptPubKey中的任何資料元素,則將outpoint序列化並插入到過濾器中。BLOOM_UPDATE_P2PUBKEY_ONLY(1)
表示只有匹配scriptPubKey中的資料元素,並且該指令碼具有標準的“pay to pubkey”或“pay to multisig”形式,才會將outpoint插入到過濾器中。
這些區別有助於避免由於錯誤的陽性率增加而使濾波器過快劣化。 我們可以觀察到,一個預計只接收標準付款地址表單的錢包的錢包不需要自動過濾器更新,因為任何花費其自己的輸出的交易在輸入中具有可預測的資料元素(公鑰 雜湊到地址)。如果錢包可能收到付費地址輸出以及付費到付費或付費多輸出,那麼BLOOM_UPDATE_P2PUBKEY_ONLY是合適的,因為它避免了最常見輸出型別的過濾器的不必要的擴充套件,但仍能確保正確的行為 付款明確指定金鑰。
顯然,nFlags == 1或nFlags == 2意味著隨著更多的鏈掃描,過濾器會變得更髒。 客戶應該監測觀察到的假陽性率,並定期用乾淨的重新整理過濾器。
部分Merkle分支格式
Merkle樹是一種將一組專案排列為樹的葉節點的方式,其中內部節點是其子雜湊連線的雜湊(hash)。 根節點被稱為Merkle根。每個比特幣塊都包含從塊交易形成的樹的Merkle根。 通過提供樹內部節點的一些元素(稱為Merkle分支),形成了一個證明,即在開採時給定的事務確實在塊中,但是證明的大小遠小於原始塊的大小。
構建一個部分Merkle樹物件
- 遍歷從根向下的merkle樹,併為每個遇到的節點:
- 檢查此節點是否對應於要包含的葉子節點(事務)或其父節點:
- 如果是這樣,附加一個“1”位元的標誌位
- 否則,追加一個“0”位元位
- 檢查此節點是否為內部節點(非葉節點)並且 是包含的葉節點的父節點:
- 如果是這樣的話:
- 下降到其左側子節點,並完全處理其下的子樹(深度優先)。
- 如果這個節點也有一個正確的子節點,那麼也進入它。
- 否則:將此節點的雜湊附加到雜湊列表。
解析部分Merkle樹物件
由於部分塊訊息包含整個塊中的事務數量,所以先前已知該Merkle樹的形狀。 再次,遍歷這棵樹,計算遍歷節點的雜湊:
- 從標誌位列表中讀一個位元位
- 如果是’0’
- 從雜湊列表中讀取雜湊值,並將其作為該節點的雜湊值返回。
- 如果它是“1”並且這是葉節點:
- 下降到其左側的子樹,並將其計算的雜湊儲存為L.
- 如果這個節點也有一個正確的子節點:
- 下降到其正確的子節點,並將其計算的雜湊儲存為R.
- 如果L == R,則部分Merkle樹物件無效。
- 返回 Hash(L || R).
- 如果此節點沒有正確的子節點,則返回Hash(L || L)。
部分merkle樹物件僅在以下情況下有效:
- 雜湊列表中的所有雜湊都被消耗掉了。
- 在標誌位列表中的所有位元位被消耗(除了填充,使之成為一個完整的位元組),並沒有更多的。
- 為根節點計算的雜湊匹配區塊頭部的merkle根。
- 區塊頭部是有效的,其聲稱工作量證明相匹配。
- 在兩個孩子的節點中,左右分支的雜湊值永遠不相等。
布隆過濾器格式
布隆過濾器是一個位域,其中根據將資料元素饋送到一組不同的雜湊函式來設定位。 所使用的雜湊函式的數量是過濾器的引數。 在比特幣中,我們使用32位Murmur雜湊函式的版本3。 為了得到N個“不同的”雜湊函式,我們只需用下面的公式初始化Murmur演算法:
nHashNum * 0xFBA4C795 + nTweak
即如果過濾器用4個雜湊函式初始化並且調整為0x00000005,則當需要第二函式(索引1)時,h1將等於4221880218。
使用 filterload
命令載入過濾器時,可以選擇兩個引數。 一個是以位元組為單位的過濾器的大小。 另一個是要使用的雜湊函式的數量。 要選擇引數,您可以使用以下公式:
假設N是你想插入到集合中的元素的數量,P是誤報的概率,其中1.0是“匹配所有”,零是無法實現的。
以位元組為單位的過濾器的大小S由(-1 / pow(log(2), 2) * N * log(P)) / 8
給出。當然,您必須確保它不超過最大大小(36,000 :因為它代表20,000個專案的過濾器,誤報率<0.1%或10,000個專案,誤報率<0.0001%)。
所需雜湊函式的數量由S * 8 / N * log(2)
給出。
版權
該檔案被放置在公共領域。