1. 程式人生 > >SPV中如何利用默克爾樹證明某個交易是否存在

SPV中如何利用默克爾樹證明某個交易是否存在

SPV 是“Simplified Payment Verification”(簡單支付驗證)的縮寫。中本聰論文簡要地提及了這一概念,指出:不執行完全節點也可驗證支付,使用者只需要儲存所有的block header就可以了。使用者雖然不能自己驗證交易,但如果能夠從區塊鏈的某處找到相符的交易,他就可以知道網路已經認可了這筆交易,而且得到了網路的多少個確認。
按照中本聰的原文,這裡有個細節需要注意,SPV指的是“支付驗證“,而不是“交易驗證”。這兩種驗證有很大區別。

“交易驗證”非常複雜,涉及到驗證是否有足夠餘額可供支出、是否存在雙花、指令碼能否通過等等,通常由執行完全節點的礦工來完成。
“支付驗證”則比較簡單,只判斷用於“支付”的那筆交易是否已經被驗證過,並得到了多少的算力保護(多少確認數)。

考慮這樣一種情況,A收到來自B的一個通知,B聲稱他已經從某某賬戶中匯款一定數額的錢給了A。去中心方式下,沒有任何人能證明B的可靠。接到這一通知,A如何能判斷B所說的是真的呢?

在比特幣系統中,這一通知是以一個固定格式的“交易”來實現的,該交易中包含B的匯款賬戶支票、B的簽名、匯給A的金額以及A的地址。

如果A想本人親自驗證這筆交易,首先,A要遍歷區塊鏈賬本,定位到B的賬戶上,這樣才能檢視B所給的賬戶支票上是否曾經有足夠的金額;接下來,A要遍歷後續的所有賬本,看B是否已經支出了這個賬戶支票上的錢給別人(是否存在雙花欺騙);然後還要驗證指令碼來判斷B是否擁有該賬戶的支配權。這一過程要求A必須得到完整的區塊鏈才行。

但是,如果A只想知道這筆支付是否已經得到了驗證(如果驗證了就發貨),他可以依賴比特幣系統來快速驗證。即,檢查發生此項支付的那筆交易是否已經收錄於區塊鏈中,並得到了多少個確認。

原理:block header中有三個關鍵欄位,一是prev_block_hash(前一區塊的hash值,確保了區塊鏈所記錄的交易次序);二是bits(當前區塊的計算難度), 三是merkle_root_hash(藉助merkle tree演算法,確保收錄與區塊中所有交易的真實性)。

驗證某個交易是否真實存在時,理論上,使用者可以通過以下方式進行驗證:
(為了簡化模型,我們假設用tx_hash來定位block。這種方法有被“交易可鍛性”攻擊的風險,實際應用中可以根據output_point來定位。)

  1. 從網路上獲取並儲存最長鏈的所有block header至本地;
  2. 計算該交易的hash值tx_hash;
  3. 定位到包含該tx_hash所在的區塊,驗證block header是否包含在已知的最長鏈中;
  4. 從區塊中獲取構建merkle tree所需的hash值;
  5. 根據這些hash值計算merkle_root_hash;
  6. 若計算結果與block header中的merkle_root_hash相等,則交易真實存在。
  7. 根據該block header所處的位置,確定該交易已經得到多少個確認。

優點:極大地節省儲存空間。減輕終端使用者的負擔。無論未來的交易量有多大,block header的大小始終不變,只有80位元組。按照每小時6個的出塊速度,每年產出52560個區塊。當只儲存block header時,每年新增的儲存需求約為4兆位元組,100年後累計的儲存需求僅為400兆,即使使用者使用的是最低端的裝置,正常情況下也完全能夠負載。
問題:如何才能通過tx_hash定位到該交易所在的區塊? 以往的比特幣協議中缺少對此的支援
需要注意的是第3點,也是筆者在實際開發過程中遇到的疑惑,其實第3點存在一個互動過程。
客戶端一般連線一個全節點,以下圖為例:
這裡寫圖片描述

所以,假設需要驗證Data01,那麼我需要得到Node12 ,Node22,Node32的Hash,這些都必須通過與全節點的互動獲得,得到這些之後,就可以依據構建規則構建樹直至根,然後比對即可確定一個交易是否存在。