Merkle Tree學習

/*最近在看Ethereum,其中一個重要的概念是Merkle Tree,之前歷來沒有據說過,因此查了些資料,學習了Merkle Tree的知識,由於接觸時間不長,對Merkle Tree的理解也不是很深刻,若是有不對的地方,但願各位大神指正*/html

Merkle Tree概念

  

  Merkle Tree,一般也被稱做Hash Tree,顧名思義,就是存儲hash值的一棵樹。Merkle樹的葉子是數據塊(例如,文件或者文件的集合)的hash值。非葉節點是其對應子節點串聯字符串的hash。[1]node

  1. Hash算法

  Hash是一個把任意長度的數據映射成固定長度數據的函數[2]。例如,對於數據完整性校驗,最簡單的方法是對整個數據作Hash運算獲得固定長度的Hash值,而後把獲得的Hash值公佈在網上,這樣用戶下載到數據以後,對數據再次進行Hash運算,比較運算結果和網上公佈的Hash值進行比較,若是兩個Hash值相等,說明下載的數據沒有損壞。能夠這樣作是由於輸入數據的稍微改變就會引發Hash運算結果的面目全非,並且根據Hash值反推原始輸入數據的特徵是困難的。[3]數據庫

  若是從一個穩定的服務器進行下載,採用單一Hash是可取的。但若是數據源不穩定,一旦數據損壞,就須要從新下載,這種下載的效率是很低的。數組

  2. Hash List安全

  在點對點網絡中做數據傳輸的時候,會同時從多個機器上下載數據,並且不少機器能夠認爲是不穩定或者不可信的。爲了校驗數據的完整性,更好的辦法是把大的文件分割成小的數據塊(例如,把分割成2K爲單位的數據塊)。這樣的好處是,若是小塊數據在傳輸過程當中損壞了,那麼只要從新下載這一快數據就好了,不用從新下載整個文件。服務器

  怎麼肯定小的數據塊沒有損壞哪?只須要爲每一個數據塊作Hash。BT下載的時候,在下載到真正數據以前,咱們會先下載一個Hash列表。那麼問題又來了,怎麼肯定這個Hash列表本事是正確的哪?答案是把每一個小塊數據的Hash值拼到一塊兒,而後對這個長字符串在做一次Hash運算,這樣就獲得Hash列表的根Hash(Top Hash or Root Hash)。下載數據的時候,首先從可信的數據源獲得正確的根Hash,就能夠用它來校驗Hash列表了,而後經過校驗後的Hash列表校驗數據塊。網絡

  3. Merkle Tree數據結構

  Merkle Tree能夠看作Hash List的泛化(Hash List能夠看做一種特殊的Merkle Tree,即樹高爲2的多叉Merkle Tree)。框架

  在最底層,和哈希列表同樣,咱們把數據分紅小的數據塊,有相應地哈希和它對應。可是往上走,並非直接去運算根哈希,而是把相鄰的兩個哈希合併成一個字符串,而後運算這個字符串的哈希,這樣每兩個哈希就結婚生子,獲得了一個」子哈希「。若是最底層的哈希總數是單數,那到最後必然出現一個單身哈希,這種狀況就直接對它進行哈希運算,因此也能獲得它的子哈希。因而往上推,依然是同樣的方式,能夠獲得數目更少的新一級哈希,最終必然造成一棵倒掛的樹,到了樹根的這個位置,這一代就剩下一個根哈希了,咱們把它叫作 Merkle Root[3]

  在p2p網絡下載網絡以前,先從可信的源得到文件的Merkle Tree樹根。一旦得到了樹根,就能夠從其餘從不可信的源獲取Merkle tree。經過可信的樹根來檢查接受到的Merkle Tree。若是Merkle Tree是損壞的或者虛假的,就從其餘源得到另外一個Merkle Tree,直到得到一個與可信樹根匹配的Merkle Tree。

  Merkle Tree和Hash List的主要區別是,能夠直接下載並當即驗證Merkle Tree的一個分支。由於能夠將文件切分紅小的數據塊,這樣若是有一塊數據損壞,僅僅從新下載這個數據塊就好了。若是文件很是大,那麼Merkle tree和Hash list都很到,可是Merkle tree能夠一次下載一個分支,而後當即驗證這個分支,若是分支驗證經過,就能夠下載數據了。而Hash list只有下載整個hash list才能驗證。

Merkle Tree的特色

 

  1. MT是一種樹,大多數是二叉樹,也能夠多叉樹,不管是幾叉樹,它都具備樹結構的全部特色;
  2. Merkle Tree的葉子節點的value是數據集合的單元數據或者單元數據HASH。
  3. 非葉子節點的value是根據它下面全部的葉子節點值,而後按照Hash算法計算而得出的。[4][5]

  

  一般,加密的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的檢索和遍歷的比較多。我也是很是困惑,一種樹結構的操做確定不只包括查找,也包括更新、插入和刪除的啊。後來查到stackexchange上的一個問題,才稍微有點明白,原文見[6]。

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

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

  而[6]中的同窗在考慮一種插入的算法,知足下面條件:

  

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

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

  根據[6]中回答者所說,Merkle Tree的插入和刪除操做實際上是一個工程上的問題,不一樣問題會有不一樣的插入方法。若是要確保樹是平衡的或者是樹高是log(n)的,能夠用任何的標準的平衡二叉樹的模式,如AVL樹,紅黑樹,伸展樹,2-3樹等。這些平衡二叉樹的更新模式能夠在O(lgn)時間內完成插入操做,而且能保證樹高是O(lgn)的。那麼很容易能夠看出更新全部的Merkle Hash能夠在O((lgn)2)時間內完成(對於每一個節點如要更新從它到樹根O(lgn)個節點,而爲了知足樹高的要求須要更新O(lgn)個節點)。若是仔細分析的話,更新全部的hash實際上能夠在O(lgn)時間內完成,由於要改變的全部節點都是相關聯的,即他們要不是都在從某個葉節點到樹根的一條路徑上,或者這種狀況相近。

  [6]的回答者說實際上Merkle Tree的結構(是否平衡,樹高限制多少)在大多數應用中並不重要,並且保持數據塊的順序也在大多數應用中也不須要。所以,能夠根據具體應用的狀況,設計本身的插入和刪除操做。一個通用的Merkle Tree插入刪除操做是沒有意義的。

Merkle Tree的應用

  

  1. 數字簽名

  最初Merkle Tree目的是高效的處理Lamport one-time signatures。 每個Lamport key只能被用來簽名一個消息,可是與Merkle tree結合能夠來簽名多條Merkle。這種方法成爲了一種高效的數字簽名框架,即Merkle Signature Scheme。

  2. P2P網絡

  在P2P網絡中,Merkle Tree用來確保從其餘節點接受的數據塊沒有損壞且沒有被替換,甚至檢查其餘節點不會欺騙或者發佈虛假的塊。你們所熟悉的BT下載就是採用了P2P技術來讓客戶端之間進行數據傳輸,一來能夠加快數據下載速度,二來減輕下載服務器的負擔。BT即BitTorrent,是一種中心索引式的P2P文件分分析通訊協議[7]

  要進下載必須從中心索引服務器獲取一個擴展名爲torrent的索引文件(即你們所說的種子),torrent文件包含了要共享文件的信息,包括文件名,大小,文件的Hash信息和一個指向Tracker的URL[8]。Torrent文件中的Hash信息是每一塊要下載的文件內容的加密摘要,這些摘要也可運行在下載的時候進行驗證。大的torrent文件是Web服務器的瓶頸,並且也不能直接被包含在RSS或gossiped around(用流言傳播協議進行傳播)。一個相關的問題是大數據塊的使用,由於爲了保持torrent文件的很是小,那麼數據塊Hash的數量也得很小,這就意味着每一個數據塊相對較大。大數據塊影響節點之間進行交易的效率,由於只有當大數據塊所有下載下來並校驗經過後,才能與其餘節點進行交易。

  就解決上面兩個問題是用一個簡單的Merkle Tree代替Hash List。設計一個層數足夠多的滿二叉樹,葉節點是數據塊的Hash,不足的葉節點用0來代替。上層的節點是其對應孩子節點串聯的hash。Hash算法和普通torrent同樣採用SHA1。其數據傳輸過程和第一節中描述的相似。

 

  3. Trusted Computing

  可信計算是可信計算組爲分佈式計算環境中參與節點的計算平臺提供端點可信性而提出的。可信計算技術在計算平臺的硬件層引入可信平臺模塊(Trusted Platform,TPM),實際上爲計算平臺提供了基於硬件的可信根(Root of trust,RoT)。從可信根出發,使用信任鏈傳遞機制,可信計算技術可對本地平臺的硬件及軟件實施逐層的完整性度量,並將度量結果可靠地保存再TPM的平臺配置寄存器(Platform configuration register,PCR)中,此後遠程計算平臺可經過遠程驗證機制(Remote Attestation)比對本地PCR中度量結果,從而驗證本地計算平臺的可信性。可信計算技術讓分佈式應用的參與節點擺脫了對中心服務器的依賴,而直接經過用戶機器上的TPM芯片來創建信任,使得建立擴展性更好、可靠性更高、可用性更強的安全分佈式應用成爲可能[10]。可信計算技術的核心機制是遠程驗證(remote attestation),分佈式應用的參與結點正是經過遠程驗證機制來創建互信,從而保障應用的安全。

  文獻[10]提出了一種基於Merkle Tree的遠程驗證機制,其核心是完整性度量值哈希樹。

  首先,RAMT 在內核中維護的再也不是一張完整性度量值列表(ML),而是一棵完整性度量值哈希樹(integrity measurement hash tree,簡稱IMHT).其中,IMHT的葉子結點存儲的數據對象是待驗證計算平臺上被度量的各類程序的完整性哈希值,而其內部結點則依據Merkle 哈希樹的構建規則由子結點的鏈接的哈希值動態生成。

  其次,爲了維護IMHT 葉子結點的完整性,RAMT 須要使用TPM 中的一段存儲器來保存IMHT 可信根哈希的值。

  再次,RAMT 的完整性驗證過程基於認證路徑(authentication path)實施.認證路徑是指IMHT 上從待驗證葉子結點到根哈希的路徑。

  4. IPFS

  IPFS(InterPlanetary File System)是不少NB的互聯網技術的綜合體,如DHT( Distributed HashTable,分佈式哈希表),Git版本控制系統,Bittorrent等。它建立了一個P2P的集羣,這個集羣容許IPFS對象的交換。所有的IPFS對象造成了一個被稱做Merkle DAG的加密認證數據結構。

  IPFS對象是一個含有兩個域的數據結構:

  • Data – 非結構的二進制數據,大小小於256kB
  • Links – 一個Link數據結構的數組。IPFS對象經過他們連接到其餘對象

  Link數據結構包含三個域:

  • Name – Link的名字
  • Hash – Link連接到對象的Hash
  • Size – Link連接到對象的累積大小,包括它的Links

  經過Name和Links,IPFS的集合組成了一個Merkle DAG(有向無環圖)。

  對於小文件(<256kB),是一個沒有Links的IPFS對象。

  對於大文件,被表示爲一個文件塊(<256kB)的集合。只有擁有最小的Data的對象來表明這個大文件。這個對象的Links的名字都爲空字符串。

  目錄結構:目錄是沒有數據的IPFS對象,它的連接指向其包含的文件和目錄。

  IPFS能夠表示Git使用的數據結構,Git commit object。Commit Object主要的特色是他有一個或多個名爲’parent0’和‘parent1’等的連接(這些連接指向前一個版本),以及一個名爲object的對象(在Git中成爲tree)指向引用這個commit的文件系統結構。

  5. BitCoin和Ethereum[12][13]

  Merkle Proof最先的應用是Bitcoin,它是由中本聰在2009年描述並建立的。Bitcoin的Blockchain利用Merkle proofs來存儲每一個區塊的交易。

  而這樣作的好處,也就是中本聰描述到的「簡化支付驗證」(Simplified Payment Verification,SPV)的概念:一個「輕客戶端」(light client)能夠僅下載鏈的區塊頭即每一個區塊中的80byte的數據塊,僅包含五個元素,而不是下載每一筆交易以及每個區塊:

  • 上一區塊頭的哈希值
  • 時間戳
  • 挖礦難度值
  • 工做量證實隨機數(nonce)
  • 包含該區塊交易的Merkle Tree的根哈希

  若是客戶端想要確認一個交易的狀態,它只需簡單的發起一個Merkle proof請求,這個請求顯示出這個特定的交易在Merkle trees的一個之中,並且這個Merkle Tree的樹根在主鏈的一個區塊頭中。

  可是Bitcoin的輕客戶端有它的侷限。一個侷限是,儘管它能夠證實包含的交易,可是它不能進行涉及當前狀態的證實(如數字資產的持有,名稱註冊,金融合約的狀態等)。

  Bitcoin如何查詢你當前有多少幣?一個比特幣輕客戶端,可使用一種協議,它涉及查詢多個節點,並相信其中至少會有一個節點會通知你,關於你的地址中任何特定的交易支出,而這可讓你實現更多的應用。但對於其餘更爲複雜的應用而言,這些遠遠是不夠的。一筆交易影響的確切性質(precise nature),能夠取決於此前的幾筆交易,而這些交易自己則依賴於更爲前面的交易,因此最終你能夠驗證整個鏈上的每一筆交易。爲了解決這個問題,Ethereum的Merkle Tree的概念,會更進一步。

  Ethereum的Merkle Proof

  每一個以太坊區塊頭不是包括一個Merkle樹,而是爲三種對象設計的三棵樹:

  • 交易Transaction
  • 收據Receipts(本質上是顯示每一個交易影響的多塊數據)
  • 狀態State

  這使得一個很是先進的輕客戶端協議成爲了可能,它容許輕客戶端輕鬆地進行並覈實如下類型的查詢答案:

  • 這筆交易被包含在特定的區塊中了麼?
  • 告訴我這個地址在過去30天中,發出X類型事件的全部實例(例如,一個衆籌合約完成了它的目標)
  • 目前個人帳戶餘額是多少?
  • 這個帳戶是否存在?
  • 假如在這個合約中運行這筆交易,它的輸出會是什麼?

  第一種是由交易樹(transaction tree)來處理的;第三和第四種則是由狀態樹(state tree)負責處理,第二種則由收據樹(receipt tree)處理。計算前四個查詢任務是至關簡單的。服務器簡單地找到對象,獲取Merkle分支,並經過分支來回復輕客戶端。

  第五種查詢任務一樣也是由狀態樹處理,但它的計算方式會比較複雜。這裏,咱們須要構建一個Merkle狀態轉變證實(Merkle state transition proof)。從本質上來說,這樣的證實也就是在說「若是你在根S的狀態樹上運行交易T,其結果狀態樹將是根爲S',log爲L,輸出爲O」 (「輸出」做爲存在於以太坊的一種概念,由於每一筆交易都是一個函數調用;它在理論上並非必要的)。

  爲了推斷這個證實,服務器在本地建立了一個假的區塊,將狀態設爲 S,並在請求這筆交易時僞裝是一個輕客戶端。也就是說,若是請求這筆交易的過程,須要客戶端肯定一個帳戶的餘額,這個輕客戶端(由服務器模擬的)會發出一個餘額查詢請求。若是須要輕客戶端在特色某個合約的存儲中查詢特定的條目,這個輕客戶端就會發出這樣的請求。也就是說服務器(經過模擬一個輕客戶端)正確迴應全部本身的請求,但服務器也會跟蹤它全部發回的數據。

  而後,服務器從上述的這些請求中把數據合併並把數據以一個證實的方式發送給客戶端。

  而後,客戶端會進行相同的步驟,但會將服務器提供的證實做爲一個數據庫來使用。若是客戶端進行步驟的結果和服務器提供的是同樣的話,客戶端就接受這個證實。

  MPT(Merkle Patricia Trees)

  前面咱們提到,最爲簡單的一種Merkle Tree大多數狀況下都是一棵二叉樹。然而,Ethereum所使用的Merkle Tree則更爲複雜,咱們稱之爲「梅克爾.帕特里夏樹」(Merkle Patricia tree)。

  對於驗證屬於list格式(本質上來說,它就是一系列先後相連的數據塊)的信息而言,二叉Merkle Tree是很是好的數據結構。對於交易樹來講,它們也一樣是不錯的,由於一旦樹已經創建,花多少時間來編輯這棵樹並不重要,樹一旦創建了,它就會永遠存在而且不會改變。

  可是,對於狀態樹,狀況會更復雜些。以太坊中的狀態樹基本上包含了一個鍵值映射,其中的鍵是地址,而值包括帳戶的聲明、餘額、隨機數nounce、代碼以及每個帳戶的存儲(其中存儲自己就是一顆樹)。例如,摩登測試網絡(the Morden testnet )的創始狀態以下所示:

  然而,不一樣於交易歷史記錄,狀態樹須要常常地進行更新:帳戶餘額和帳戶的隨機數nonce常常會更變,更重要的是,新的帳戶會頻繁地插入,存儲的鍵( key)也會常常被插入以及刪除。咱們須要這樣的數據結構,它能在一次插入、更新、刪除操做後快速計算到樹根,而不須要從新計算整個樹的Hash。這種數據結構一樣得包括兩個很是好的第二特徵:

  • 樹的深度是有限制的,即便考慮攻擊者會故意地製造一些交易,使得這顆樹儘量地深。否則,攻擊者能夠經過操縱樹的深度,執行拒絕服務攻擊(DOS attack),使得更新變得極其緩慢。
  • 樹的根只取決於數據,和其中的更新順序無關。換個順序進行更新,甚至從新從頭計算樹,並不會改變根。

  MPT是最接近同時知足上面的性質的的數據結構。MPT的工做原理的最簡單的解釋是,值經過鍵來存儲,鍵被編碼到搜索樹必需要通過的路徑中。每一個節點有16個孩子,所以路徑又16進制的編碼決定:例如,鍵‘dog’的16進制編碼是6 4 6 15 6 7,因此從root開始到第六個分支,而後到第四個,再到第六個,再到第十五個,這樣依次進行到達樹的葉子。

  在實踐中,當樹稀少時也會有一些額外的優化,咱們會使過程更爲有效,但這是基本的原則。

  6. 其餘應用

  用到Merkle Tree的應用還有不少,好比Git,Amazon Dynamo,Apache Wave Protocol,Tahoe-LAFS backup system,Certificate Transparency framework,NoSQL systems like Apache Cassadra and Riak等

參考

[1]  https://en.wikipedia.org/wiki/Merkle_tree

[2]  https://en.wikipedia.org/wiki/Hash_function#Hash_function_algorithms

[3]  http://www.jianshu.com/p/458e5890662f

[4]  http://blog.csdn.net/xtu_xiaoxin/article/details/8148237

[5]  http://blog.csdn.net/yuanrxdu/article/details/22474697?utm_source=tuicool&utm_medium=referral

[6]  http://crypto.stackexchange.com/questions/22669/merkle-hash-tree-updates

[7]  https://en.wikipedia.org/wiki/BitTorrent

[8]  梁成仁, 李健勇, 黃道穎, 等. 基於 Merkle 樹的 BT 系統 torrent 文件優化策略[J]. 計算機工程, 2008, 34(3): 85-87.

[9]  http://bittorrent.org/beps/bep_0030.html

[10] 徐梓耀, 賀也平, 鄧靈莉. 一種保護隱私的高效遠程驗證機制[J]. Journal of Software, 2011, 22(2).

[11] http://whatdoesthequantsay.com/2015/09/13/ipfs-introduction-by-example/

[12] https://www.weusecoins.com/what-is-a-merkle-tree/

[13] http://www.8btc.com/merkling-in-ethereum

相關文章
相關標籤/搜索