1. 程式人生 > >七、區塊鏈如何運用merkle tree驗證交易真實性

七、區塊鏈如何運用merkle tree驗證交易真實性

本文假設你已經知道區塊鏈中merkle tree的原理,現在搞明白具體怎麼來實現交易真實性驗證。

Merkle Tree

這個小節簡述一下merkle的原理。簡單說,merkle tree就是一個hash二叉樹,父節點是兩個子節點的double sha256的結果,葉子節點就是交易的content的double sha256結果。

上圖中最下面那一層就是交易資料,每一個交易都可以計算出一個hash,從而層層向上,得到merkle root。但是由於blockchain裡面都merkle運算要求葉子節點是偶數,所以,當一個區塊內包含當交易數量為奇數時,把最後一個交易複製一份,湊成偶數。

最後就是把merkle root儲存在區塊頭中,交易資料被儲存在區塊體中,其實中間當那些hash並沒有被儲存,它們只是運算過程資料。

SPV

為什麼要搞這麼複雜?直接把所有交易資料儲存起來了,要驗證交易是否存在還不簡單嗎?之所以要這麼幹,是因為比特幣發明之初,中本聰想到有一種輕錢包的設計,這就是SPV(簡化支付驗證,Simplified Payment Verification)。

輕錢包並不儲存完整的區塊鏈,而是隻儲存每一個區塊的區塊頭。區塊體儲存了完整的交易資訊,而交易資訊需要的儲存量大部分都是交易頭的千倍以上。所以,如果只儲存交易頭,就可以極大的減少本地客戶端儲存的區塊鏈資訊。

但是,不能因此讓區塊鏈無法工作啊。如果這個時候輕錢包要對某一個交易進行驗證,而本地又沒有這個交易的資訊,那怎麼驗證呢?這時,區塊頭裡面的merkle root就要起作用了。

驗證路徑

在講述輕錢包的驗證過程之前,我們需要知道如何在merkle tree裡面做驗證。我們已知merkle tree裡面父節點和子節點的運算關係,因此,當我們要證明一個葉子節點存在於這棵樹時,只需要得到從該葉子節點到根的運算過程裡面需要的那些hash即可,並不需要所有葉子節點參與計算。

你可能覺得有點奇怪,為什麼不直接把所有的葉子節點告訴它就行了,你用所有葉子節點能算出root hash就驗證通過了。但事實就是這樣,因為每一個父節點hash一定是由兩個子節點hash運算得到,所以,我們只需要挑選出所有參與運算的節點,就可以證明這個葉子節點存在於樹中。這樣可以減少hash運算的次數。而這些被挑選出來的節點,以及它們之間的層級關係,就是驗證路徑,即上圖中merkle root那個盒子下面的所有盒子。

如何證明交易的真實性?

比特幣網路中的交易,只有已經被記錄到區塊鏈,並且已經得到6個確認的,才被認為是真實的,只有基於這些真實交易發起的新交易(輸入與輸出的概念),才是合法的。

我們詢問一個交易是否真實,往往基於以下前提:

  • 我們在問一個交易是否已被記錄到區塊鏈中
  • 而且這個交易所在的區塊鏈是最長的哪一條,沒有在分叉鏈上
  • 當每個節點接收到一條交易廣播時,我們要查詢作為一筆新交易的輸入的真實性
  • 礦工對交易進行打包之前,對所有的輸入進行真實性驗證(在礦工接收到交易資訊時就已經驗證過了,打包的時候驗證2000條交易資訊不可能)

那麼對於SPV輕錢包而言,怎麼知道一個交易是否真實的呢?SPV拿到一個交易資訊之後(比如接收到一筆錢),並不能確認這個交易是否合法,因此要對這個交易的輸入進行驗證。但是它只拿到了單個交易的資訊,而沒有本地的完整區塊鏈資料,因此,SPV要拿著這個交易的資訊向網路發起查詢請求,這個請求被稱為merkle block message。當其他有完整區塊鏈資料的客戶端收到這個請求之後,利用傳過來的交易資訊在自己的區塊鏈資料庫中進行查詢,並把驗證路徑返回給請求源,SPV拿到驗證路徑之後,再做一次merkle校驗,確認無誤之後,就認為這個交易是可信的。

現在的問題是:

  • 怎麼從區塊鏈裡面查一個交易?
  • 怎麼獲取merkle驗證路徑?
  • 怎麼確保網路上這個返回的驗證路徑不是偽造的?

從區塊鏈查交易

區塊鏈的資料結構是離散的,比特幣裡面一個區塊被儲存在一個檔案裡面,要得到一個交易的驗證路徑,必須得到這個交易所在的區塊鏈。這是一個複製的查詢過程,可能需要把所有的區塊都遍歷一遍才能找到。因此,blockchain.info這樣的網站孕育而生,幫助你通過一個資訊查這個資訊在區塊鏈上的所有相關記錄。但是對於客戶端而言,可沒那麼容易,它不能信任blockchain.info這個網站,只能信任自己本地儲存的區塊鏈。所以,只能用比較合理的演算法,去優化交易查詢。

一種設計是,把每一個區塊的資料結構修改為關係型資料庫,通過關係型資料庫,可以用sql語句快速查詢。但是,要遍歷查詢所有區塊鏈,是比較浪費的。還有一種想法是,利用交易的時間戳來快速定位區塊位置,在臨近的幾個區塊中快速找到它。

如何獲取merkle驗證路徑?

實際上,merkle的驗證路徑生成的前提是已經存在一棵完整的merkle樹。市面上有很多merkle樹的實現包,有的包直接給出來getProof的方法來獲取某個葉子節點的驗證路徑。

在客戶端收到merkle block message之後,要執行下面的步驟:

  1. 通過上述方法找到包含該交易的區塊
  2. 檢查該區塊是否是整個網路中最長鏈條裡面的
  3. 取出所有交易生成merkle tree,利用getProof方法得到該交易的驗證路徑
  4. 將該驗證路徑傳送回請求源

SPV得到響應之後,要做如下驗證:

  1. 同步區塊鏈,確保是整個網路中最長的一條
  2. 先拿到merkle root去區塊鏈中查詢,確保該merkle root hash是在鏈條中
  3. 利用拿到的驗證路徑,再進行一次merkle校驗,確保驗證路徑全部合法

為什麼SPV還要再做一次merkle校驗呢?主要是為了確保響應方傳送的驗證路徑的有效性。

確保驗證路徑的真實性

上面提到了SPV還要做一次merkle校驗,這也是“不信任”的表現之一。我們並不確保響應我們的節點不會作弊或欺詐,因此,我們要自己進行校驗。但是,有沒有可能雖然校驗過程順利,但是實際上校驗路徑是偽造的呢?

我們來做一個假設:1)merkle root為真;2)交易為假;3)路徑中的hash可真可假。這個假設是否成立?

我們知道,不同字串碰撞到同一個sha256的概率極小,那麼double sha256的概率就是它的平方,而merkle root是經過一層一層計算上來的,如果一個區塊只有一個(或2個)交易,那麼就是double^(2+1) sha256,而如果是4個交易,就有double^(4 + 2 + 1) sha256,更何況一個區塊有那麼多交易,要經過merkle運算得到一個相同的hash,幾乎是不可能的,因此,在merkle驗證中用一個偽造的交易hash來得到一個已知來merkle root是不可能的。

如果還想更進一步校驗,可以在區塊頭中儲存區塊打包的交易的數量,這樣就可以知道從交易hash到merkle root需要經過幾層的運算。這也是一個檢驗點。

小結

merkle tree被廣泛運用於區塊鏈中,但並不是只有區塊鏈使用它來進行校驗。比如一些p2p下載,如迅雷,就需要把檔案分割為小塊檔案,每塊都有一個hash,每塊從不同的網路節點下載,最後組成一個完整的檔案,但是也需要進行hash驗證,它也可以使用merkle來進行驗證。merkle tree也不一定是二叉樹,可以是任意樹結構。而在以太坊中,merkle驗證還不夠用,增加了Patricia Tree驗證,合起來稱為“Merkle Patricia Tree”。