Merkle 樹結構
默克爾樹(又叫雜湊樹)是一種典型的二叉樹結構,有一個根節點、一組中間節點和一組葉節點組成。默克爾樹最早由 Merkle Ralf 在 1980 年提出,曾廣泛用於檔案系統和P2P系統中。比如 git
、區塊鏈
、IPFS
等。
主要特點:
- 最下面的葉節點包含儲存資料或其雜湊值;
- 非葉子節點(包括中間節點和根節點)都是它的兩個孩子節點內容的雜湊值
默克爾樹可以推廣到多叉樹的情形,此時非葉子節點的內容為它所有的孩子節點的內容的雜湊值。
默克爾樹逐層記錄雜湊值的特點,讓它具有了一些獨特的性質。例如,底層資料的任何變動,都會傳遞到其父節點,一層層沿著路徑一直到樹根。這意味著根的值實際上代表了對底層所有資料的“數字摘要”。
應用場景
目前,默克爾樹的典型應用場景包括以下幾種。
證明某個集合中存在或不存在某個元素
透過構建集合的默克爾樹,並提供該元素各級兄弟節點中的 hash 值,可以不暴露集合完整內容而證明某元素存在。
另外,對於可以進行排序的集合,可以將不存在元素的位置用空值代替,以此構建稀疏默克爾樹(Sparse Merkle Tree)。該結構可以證明某個集合中不包括指定元素。
快速比較大量資料
對每組資料排序後構建默克爾樹結構。當兩個默克爾樹根相同時,則意味著所代表的兩組資料必然相同。否則,必然不同。
由於 hash 計算的過程可以十分快速,預處理可以在短時間內完成。利用默克爾樹結構能帶來巨大的比較效能優勢
快速定位修改
以下圖為例,基於資料 D0……D3 構造默克爾樹,如果 D1 中資料被修改,會影響到 N1,N4 和 Root。
因此,一旦發現某個節點如 Root 的數值發生變化,沿著 Root –> N4 –> N1,最多透過 O(lgN) 時間即可快速定位到實際發生改變的資料塊 D1。
零知識證明
它指的是證明者能夠在不向驗證者提供任何有用的資訊的情況下(沒有洩露資訊),使驗證者相信某個論斷是正確的。
有個很簡單的例子:A 要向 B 證明自己擁有某個房間的鑰匙,假設該房間只能用鑰匙開啟鎖,而其他任何方法都打不開。這時有兩個方法:
- A 把鑰匙出示給 B, B 用這把鑰匙開啟該房間的鎖,從而證明 A 擁有該房間的正確鑰匙。
- B 確定該房間內有某一物體,A 用自己擁有的鑰匙開啟該房間的門,然後把物體拿出來出示給 B,從而證明自己確實擁有該房間的鑰匙。
後面的第二種方法屬於零知識證明。它的好處在於,整個證明的過程中,B 始終不能看到鑰匙的樣子,從而避免了鑰匙的洩漏。
在默克爾樹中,我們仍舊以上圖為例,如何向他人證明我擁有 D0
這個資料,而不用暴露更多系統的資訊呢?模仿上面的例子,驗證者隨機提供資料 D1
、D2
和 D3
,證明者構造如圖的默克爾樹,並公佈 N1
、N5
和 Root
。驗證者自行計算 Root
值,看是否一致,從而檢驗 D0
是否存在,因為如果存在,N0
一定相同,那麼 N4(N0-N1)
也一定相同、Root(N4-N5)
也一定相同。整個過程中驗證著沒有得到任何除了 D0
外的敏感資訊(其他的 D
)。
程式碼示例
以下展示部分程式碼,完整程式碼
// Content 代表一個資料塊
type Content interface {
CalculateHash() ([]byte, error)
Equals(other Content) (bool, error)
}
// MerkleTree 默克爾樹
type MerkleTree struct {
Root *Node // 根節點
merkleRoot []byte // 根節點的雜湊值
Leafs []*Node // 所有的葉子節點
hashStrategy func() hash.Hash // 計算雜湊的方法
}
// Node 表示默克爾樹中的葉節點、非葉節點或 Root
type Node struct {
Tree *MerkleTree // 所在的 Merkle Tree
Parent *Node // 父節點
Left *Node // 左孩子
Right *Node // 右孩子
leaf bool // 是否葉子節點
dup bool // 是否複製節點
Hash []byte // 如果是葉子節點,則為葉子節點資料的雜湊值;如果非葉子節點,則為左右孩子雜湊值組合後的雜湊值
C Content // 葉子節點儲存的資料塊
}
引用:Merkle 樹結構
本作品採用《CC 協議》,轉載必須註明作者和本文連結