1. 程式人生 > >以太坊MPT原理,你最值得看的一篇

以太坊MPT原理,你最值得看的一篇

    MPT的全稱是Merkle Patricia Tree, 從這裡可以看出MPT是Merkle Tree + Patricia Tree。接下來就就來講講這兩種樹:

Merkle Tree

       區塊鏈P2P網路中,如果需要傳輸的資料很大,就需要同時從多個機器上下載資料,而且很可能有些機器是不穩定(可能下載速度很慢)或者不可信的(需要重新下載)。為了快速下載大塊資料並驗證,更好的辦法是把大檔案分割成小的資料塊(例如把大檔案分割成4K為單位的小資料塊)。這樣的好處是如果小塊資料在傳輸過程中損壞了或者是錯誤的資料,那麼只要重新下載這一塊資料就行了,不用重新下載整個檔案。    由於只有大檔案內容的hash, 當其中一塊小資料錯誤時,我們是能檢出由小塊資料拼湊出來的大資料是錯誤的,但是我們不知道哪塊小資料是錯誤的,就沒法通過重傳錯誤的小資料來糾正。哪怎麼處理呢?為每個小塊生成hash, 節點先把小塊資料的hash都下載下來,然後就可以驗證一個一個驗證小塊資料是否正確了。那問題又來了,小塊資料的hash的正確性誰來保證呢?
信任節點,比如BT論壇上的bt種子檔案,這個種子檔案就記錄了原始檔案的小塊資料的hash. 驗證問題解決了,但是多出來了小塊資料hash,當塊資料很大時,這個資料量也不小。因而Merkle Tree出來了,它就是用來解決這個問題的。
    小塊資料的hash兩兩組合再次生成新的hash,然後新生成的hash又兩兩合併生成更新的hash,直至最後兩個hash生成一個hash root,這個叫merkle root(默克爾根)。可見merkle tree和傳統bt分片技術只是對小塊資料hash的組織方式不一樣。
  看到上面的圖,你可能會說,merkle tree不是生成更多hash資料了啊,怎麼能降低資料傳輸量。確實,對於資料傳送方來說,相比傳統分片技術,它是需要儲存完整merkle tree, 會多佔用一點空間。但是對於接受方來說,它在驗證某一塊資料不需要下載全部hash,只需一段merkle 路徑即可,比如下圖

   如果要驗證slice2資料的正確性,只需要拿到hash1, h12, h02這3個hash再加上本地儲存的root hash,就可以驗證了。需要傳輸的hash資料量從n變為log2n.

    總結,Merkle root(包含在區塊頭裡)是從可信渠道下載的(主鏈或者可信節點),接收到資料及這個資料對應的Merkle驗證路徑即可驗證該資料的正確性。

Patricia Tree

    patricia tree 字首樹,是一種編碼方式,它是傳統trie的改進。

Trie樹

    Trie,又稱字首樹或字典樹,是一種有序樹狀的資料結構,其中的鍵通常是字串,常用語儲存Key-value資料結構。    Trie與二叉查詢樹不同,鍵不是直接儲存在節點中,而是由節點在樹中的位置決定。一個節點的所有子孫都有相同的字首,節點對應的key是根節點到該節點路徑上的所有節點key值前後拼接而成,節點的value值就是該key對應的值。根節點對應空字串key。
    如果key是英文單詞,trie的每個節點就是一個長度為27的指標陣列,index0-25代表a-z字元,26為標誌域。    
上面的儲存的資料如下:[‘a'] = V1, [‘ab’] = V3, [‘b’] = V2, [‘ba’] = V4,  [‘baa’] = V5, [‘zaaba] = V6從上面可以看出zaaba這個key,沒有和任何其他key共享欄位,但是卻產生了6層,這種無用的深度增加有什麼方法減呢?Patricia Tree就可以解決這個問題

Merkle Patricia Tree對trie的改進

    上面tries出現的問題的根本原因是每個前置節點只能表示一個字母,key有多長,樹的深度就會多長,不管這個key有沒有和其他key共享部分key。因而允許一個節點表示變長的key就可以解決這個深度,具體以官方的下圖為例:
上圖儲存的key-value如下:
從前面結構圖可以看出,Merkle Patricia Tree有4種類型的節點:
  • 葉子節點(leaf),表示為[key,value]的一個鍵值對。和前面的英文字母key不一樣,這裡的key都是16編碼出來的字串,每個字元只有0-f 16種,value是RLP編碼的資料
  • 擴充套件節點(extension),也是[key,value]的一個鍵值對,但是這裡的value是其他節點的hash值,通過hash連結到其他節點
  •  分支節點(branch),因為MPT樹中的key被編碼成一種特殊的16進位制的表示,再加上最後的value,所以分支節點是一個長度為17的list,前16個元素對應著key中的16個可能的十六進位制字元,如果有一個[key,value]對在這個分支節點終止,最後一個元素代表一個值,即分支節點既可以搜尋路徑的終止也可以是路徑的中間節點。分支節點的父親必然是extension node
  • 空節點,程式碼中用null表示

原理解釋

插入第一個<a711355, 45>,由於只有一個key,直接用leaf node既可表示        
接著插入a77d337,由於和a711355共享字首’a7’,因而可以建立’a7'擴充套件節點。
接著插入a7f9365,也是共享’a7’,只需新增一個leaf node.
最後插入a77d397,這個key和a77d337共享’a7’+’d3’,因而再需要建立一個’d3’擴充套件節點

MPT中的Merkle Tree哪去了

    前面為了和官方圖一致,將葉子節點和最後的short node合併到一個節點了,事實上原始碼實現需要再深一層,最後一層的葉子節點只有資料    
MPT節點有個flag欄位,flag.hash會儲存該節點採用merkle tree類似演算法生成的hash,同時會將hash和源資料以<hash, node.rlprawdata>方式儲存在leveldb資料庫中。這樣後面通過hash就可以反推出節點資料。具體結構如下(藍色的hash部分就是flag.hash欄位)

    這樣一個結構的核心思想是:hash可以還原出節點上的資料,這樣只需要儲存一個root(hash),即可還原出完整的樹結構,同時還可以按需展開節點資料,比如如果只需要訪問<a771355, 45>這個資料,只需展開h00, h10, h20, h30這四個hash對應的節點
/********************************* 本文來自CSDN博主"愛踢門"******************************************/