在計算機領域,Merkle樹大多用來進行完整性驗證處理。在處理完整性驗證的應用場景中,特別是在分佈式環境下進行這樣的驗證時,Merkle樹會大大減小數據的傳輸量以及計算的複雜度。html
Merkle哈希樹是一類基於哈希值的二叉樹或多叉樹,其葉子節點上的值一般爲數據塊的哈希值,而非葉子節點上的值是將該節點的全部子節點的組合結果的哈希值。node
以下圖所示爲一個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 Treespa
加入最底層有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的結構是這樣的:
而有的同窗在考慮一種插入的算法,知足下面條件:
而後上面的插入結果就會變成這樣:
因此,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才能驗證。
【時間倉促,若有錯誤,歡迎指正! || 歡迎留下您的評語! 你們一塊兒探討、學習區塊鏈!】
【轉載請註明出處!http://www.cnblogs.com/X-knight/】
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 http://www.javashuo.com/article/p-tjfzdjhk-bs.html