-h stack get 伸展樹 答案 logs 數控 不同的 中間

  在計算機領域,Merkle樹大多用來進行完整性驗證處理。在處理完整性驗證的應用場景中,特別是在分布式環境下進行這樣的驗證時,Merkle樹會大大減少數據的傳輸量以及計算的復雜度。

  Merkle哈希樹是一類基於哈希值的二叉樹或多叉樹,其葉子節點上的值通常為數據塊的哈希值,而非葉子節點上的值是將該節點的所有子節點的組合結果的哈希值。

  如下圖所示為一個Merkle哈希樹,節點A的值必須通過節點C、D上的值計算而得到。葉子節點C、D分別存儲數據塊001和002的哈希值,而非葉子節點A存儲的是其子節點C、D的組合的哈希值,這類非葉子節點的哈希值被稱作路徑哈希值,而葉子節點的哈希值是實際數據的哈希值。

技術分享圖片

  當數據從A端傳到B端時,為了檢驗數據的完整性,只需要驗證A、B端上所構造的Merkle樹的根節點是否一致即可。若一致,表示數據在傳輸過程中沒有發生改變。若不一致,說明數據在傳輸過程中被修改。而且通過Merkle樹很容易定位找到被篡改的節點。定位的時間復雜度為O(log(n))。

  比特幣的輕量級節點所采用的SPV驗證就是利用Merkle樹這一優點。

  區塊鏈中的Merkle樹是二叉樹,用於存儲交易信息。每個交易兩兩配對,構成Merkle樹的葉子節點,進而生成整個Merkle樹。Merkle樹使得用戶可以通過從區塊頭得到的Merkle樹根和別的用戶所提供的中間哈希值列表去驗證某個交易是否包含在區塊中。提供中間哈希值的用戶並不需要是可信的,因為偽造區塊頭的代價很高,而中間哈希值如果偽造的話會導致驗證失敗。

  通常,加密的hash方法像SHA-2和MD5用來做Hash。但如果僅僅防止數據不是蓄意的損壞或篡改,可以改用一些安全性低但效率高的校驗和算法,如CRC。

  Second Preimage Attack: Merkle tree的樹根並不表示樹的深度,這可能會導致second-preimage attack,即攻擊者創建一個具有相同Merkle樹根的虛假文檔。一個簡單的解決方法在Certificate Transparency中定義:當計算葉節點的hash時,在hash數據前加0x00。當計算內部節點是,在前面加0x01。另外一些實現限制hash tree的根,通過在hash值前面加深度前綴。因此,前綴每一步會減少,只有當到達葉子時前綴依然為正,提取的hash鏈才被定義為有效。

Merkle tree操作:

  1.創建Merckle Tree

  加入最底層有9個數據塊。

  step1:(紅色線)對數據塊做hash運算,Node0i = hash(Data0i), i=1,2,…,9

  step2: (橙色線)相鄰兩個hash塊串聯,然後做hash運算,Node1((i+1)/2) = hash(Node0i+Node0(i+1)), i=1,3,5,7;對於i=9, Node1((i+1)/2) = hash(Node0i)

  step3: (黃色線)重復step2

  step4:(綠色線)重復step2

  step5:(藍色線)重復step2,生成Merkle Tree Root

技術分享圖片

  易得,創建Merkle Tree是O(n)復雜度(這裏指O(n)次hash運算),n是數據塊的大小。得到Merkle Tree的樹高是log(n)+1。

  2.檢索數據塊 

  為了更好理解,我們假設有A和B兩臺機器,A需要與B相同目錄下有8個文件,文件分別是f1 f2 f3 ....f8。這個時候我們就可以通過Merkle Tree來進行快速比較。假設我們在文件創建的時候每個機器都構建了一個Merkle Tree。具體如下圖:

技術分享圖片

  從上圖可得知,葉子節點node7的value = hash(f1),是f1文件的HASH;而其父親節點node3的value = hash(v7, v8),也就是其子節點node7 node8的值得HASH。就是這樣表示一個層級運算關系。root節點的value其實是所有葉子節點的value的唯一特征。

  假如A上的文件5與B上的不一樣。我們怎麽通過兩個機器的merkle treee信息找到不相同的文件? 這個比較檢索過程如下:

  Step1. 首先比較v0是否相同,如果不同,檢索其孩子node1和node2.

  Step2. v1 相同,v2不同。檢索node2的孩子node5 node6;

  Step3. v5不同,v6相同,檢索比較node5的孩子node 11 和node 12

  Step4. v11不同,v12相同。node 11為葉子節點,獲取其目錄信息。

  Step5. 檢索比較完畢。

  以上過程的理論復雜度是Log(N)。 

  3. 更新,插入和刪除

  雖然網上有很多關於Merkle Tree的資料,但大部分沒有涉及Merkle Tree的更新、插入和刪除操作,討論Merkle Tree的檢索和遍歷的比較多。顯然,一種樹結構的操作肯定不僅包括查找,也包括更新、插入和刪除的啊。後來查到風之舞555的總結的文章,少有感悟,下面引用風之舞555對該部分講述:

  對於Merkle Tree數據塊的更新操作其實是很簡單的,更新完數據塊,然後接著更新其到樹根路徑上的Hash值就可以了,這樣不會改變Merkle Tree的結構。但是,插入和刪除操作肯定會改變Merkle Tree的結構,如下圖,一種插入操作是這樣的:

技術分享圖片

  插入數據塊0後(考慮數據塊的位置),Merkle Tree的結構是這樣的:

技術分享圖片

  而有的同學在考慮一種插入的算法,滿足下面條件: 

  • re-hashing操作的次數控制在log(n)以內
  • 數據塊的校驗在log(n)+1以內
  • 除非原始樹的n是偶數,插入數據後的樹沒有孤兒,並且如果有孤兒,那麽孤兒是最後一個數據塊
  • 數據塊的順序保持一致
  • 插入後的Merkle Tree保持平衡

  然後上面的插入結果就會變成這樣:

技術分享圖片

  所以,Merkle Tree的插入和刪除操作其實是一個工程上的問題,不同問題會有不同的插入方法。如果要確保樹是平衡的或者是樹高是log(n)的,可以用任何的標準的平衡二叉樹的模式,如AVL樹,紅黑樹,伸展樹,2-3樹等。這些平衡二叉樹的更新模式可以在O(lgn)時間內完成插入操作,並且能保證樹高是O(lgn)的。那麽很容易可以看出更新所有的Merkle Hash可以在O((lgn)2)時間內完成(對於每個節點如要更新從它到樹根O(lgn)個節點,而為了滿足樹高的要求需要更新O(lgn)個節點)。如果仔細分析的話,更新所有的hash實際上可以在O(lgn)時間內完成,因為要改變的所有節點都是相關聯的,即他們要不是都在從某個葉節點到樹根的一條路徑上,或者這種情況相近。

  實際上Merkle Tree的結構(是否平衡,樹高限制多少)在大多數應用中並不重要,而且保持數據塊的順序也在大多數應用中也不需要。因此,可以根據具體應用的情況,設計自己的插入和刪除操作。一個通用的Merkle Tree插入刪除操作是沒有意義的。


拓展知識:

  Hash List 與 Merkle tree 有什麽異同?

娓娓道來~~~~~~~

  網絡傳輸數據的時候,A收到B的傳過來的文件,需要確認收到的文件有沒有損壞。如何解決?

  :有一種方法是B在傳文件之前先把文件的hash結果給A,A收到文件再計算一次哈希然後和收到的哈希比較就知道文件有無損壞。

  但是當文件很大的時候,往往需要把文件拆分很多的數據塊各自傳輸,這個時候就需要知道每個數據塊的哈希值。怎麽辦呢?

  :這種情況,可以在下載數據之前先下載一份哈希列表(hash list),這個列表每一項對應一個數據塊的哈希值。對這個hash list拼接後可以計算一個根hash。實際應用中,我們只要確保從一個可信的渠道獲取正確的根hash,就可以確保下載正確的文件。

  但是基於hash list的方案這樣一個問題: 數據塊很多的時候,往往遍歷所有數據塊的Hash List代價比較大。

  有沒有一種方法可以通過部分Hash就能校驗整個文件的完整性呢?

  :答案是肯定的!Merkle Tree 就能做到!

  Merkle Tree和Hash List的主要區別是,可以直接下載並立即驗證Merkle Tree的一個分支。因為可以將文件切分成小的數據塊,這樣如果有一塊數據損壞,僅僅重新下載這個數據塊就行了。如果文件非常大,那麽Merkle tree和Hash list都很大,但是Merkle tree可以一次下載一個分支,然後立即驗證這個分支,如果分支驗證通過,就可以下載數據了。而Hash list只有下載整個hash list才能驗證。

REFERENCE

1.Merkle Tree 學習 http://www.cnblogs.com/fengzhiwu/p/5524324.html

2. Merkle Tree 增刪數據http://crypto.stackexchange.com/questions/22669/merkle-hash-tree-updates

3.Merkle Tree、Hash List https://blog.csdn.net/pony_maggie/article/details/74538902

1.3.2 區塊鏈中的密碼學——Merkle 樹