快速學習-帕特里夏樹

帕特里夏樹(Patricia Tree)

如果一個基數樹的“基數”(radix)爲2或2的整數次冪,就被稱爲“帕特里夏樹”,有時也直接認爲帕特里夏樹就是基數樹

  • 以太坊中採用 Hex 字符作爲 key 的字符集,也就是基數爲16 的帕特里夏樹
  • 以太坊中的樹結構,每個節點可以有最多 16 個子節點,再加上 value,所以共有 17 個“插槽”(slot)位置
  • 以太坊中的帕特里夏樹加入了一些額外的數據結構,主要是爲了解決效率問題

MPT(Merkel Patricia Tree)

  • 梅克爾-帕特里夏樹是梅克爾樹和帕特里夏樹的結合
  • 以太坊中的實現,對 key 採用 Hex 編碼,每個 Hex 字符就是一個nibble(半字節)
  • 遍歷路徑時對一個節點只訪問它的一個 nibble ,大多數節點是一個包含17個元素的數組;其中16個分別以 hex字符作爲索引值,存儲路徑中下一個 nibble 的指針;另一個存儲如果路徑到此已遍歷結束,需要返回的最終值。這樣的節點叫做“分支節點”(branch node)
  • 分支節點的每個元素存儲的是指向下一級節點的指針。與傳統做法不同,MPT 是用所指向節點的 hash 來代表這個指針的;每個節點將下個節點的 hash 作爲自己存儲內容的一部分,這樣就實現了 Merkel 樹結構,保證了數據校驗的有效性

MPT 節點分類

MPT 中的節點有以下幾類:

  • 空節點(NULL)
  • 表示空字符串
  • 分支節點(branch)
  • 17 個元素的節點,結構爲 [ v0 … v15, vt ]
  • 葉子節點(leaf)
  • 擁有兩個元素,編碼路徑 encodedPath 和值 value
  • 擴展節點(extension)
  • 擁有兩個元素,編碼路徑 encodedPath 和鍵 key

MPT 中數據結構的優化

  • 對於64個字符的路徑長度,很有可能在某個節點處會發現,下面至少有一段路徑沒有分叉;這很難避免
  • 我們當然可以依然用標準的分支節點來表示,強制要求這個節點必須有完整的16個索引,並給沒有用到的那15個位置全部賦空值;但這樣有點蠢
  • 通過設置“擴展節點”,就可以有效地縮短訪問路徑,將冗長的層級關係壓縮成一個鍵值對,避免不必要的空間浪費
  • 擴展節點(extension node)的內容形式是 [encodedPath, key],其中 encodedPath 包含了下面不分叉的那部分路徑,key 是指向下一個節點的指針(hash,也即在底層db中的存儲位置)
  • 葉子節點(leaf node):如果在某節點後就沒有了分叉路徑,那這是一個葉子節點,它的第二個元素就是自己的 value

壓縮之後的 ”dog” 路徑

在這裏插入圖片描述

緊湊編碼(compact coding)

路徑壓縮的處理相當於實現了壓縮前綴樹的功能;不過路徑表示是 Hex 字符串(nibbles),而存儲卻是以字節(byte)爲單位的,這相當於浪費了一倍的存儲空間

  • 我們可以採用一種緊湊編碼(compact coding)方式,將兩個 nibble 整合在一個字節中保存,這就避免了不必要的浪費
  • 這裏就會帶來一個問題:有可能 nibble 總數是一個奇數,而數據總是以字節形式存儲的,所以無法區分 nibble 1 和nibbles 01;這就使我們必須分別處理奇偶兩種情況
  • 爲了區分路徑長度的奇偶性,我們在 encodedPath 中引入標識位

Hex 序列的壓縮編碼規則

  • 我們在 encodedPath 中,加入一個 nibble 作爲前綴,它的後兩位用來標識節點類型和路徑長度的奇偶性
    在這裏插入圖片描述
  • MPT 中還有一個可選的“結束標記”(用T表示),值爲0x10 (十進制的16),它僅能在路徑末尾出現,代表節點是一個最終節點(葉子節點)
  • 如果路徑是奇數,就與前綴 nibble 湊成整字節;如果是偶數,則前綴 nibble 後補 0000 構成整字節

編碼示例

> [ 1, 2, 3, 4, 5, ...] 不帶結束位,奇路徑
• '11 23 45'> [ 0, 1, 2, 3, 4, 5, ...] 不帶結束位,偶路徑
• '00 01 23 45'> [ 0, f, 1, c, b, 8, 10] 帶結束位 T 的偶路徑
• '20 0f 1c b8'> [ f, 1, c, b, 8, 10] 帶結束位 T 的奇路徑
• '3f 1c b8'

MPT 樹結構示例

• 假設我們現在要構建一個存儲了以下鍵值對的 MPT 樹:

('do', 'verb'), ('dog', 'puppy'), ('doge', 'coin'), ('horse', 'stallion')

• 首先我們會把所有的路徑(path)轉成 ASCII 碼錶示的 bytes: • <64 6f> : ‘verb’

<64 6f 67> : 'puppy'<64 6f 67 65> : 'coin'<68 6f 72 73 65> : 'stallion‘

• 然後我們就可以用在底層db中存儲的以下鍵值對,構建出 MPT 樹:

• rootHash: [ <16>, hashA ] 
• hashA: [ <>, <>, <>, <>, hashB, <>, <>, <>, hashC, <>, <>, <>, <>, <>, <>, <>, <> ] 
• hashC: [ <20 6f 72 73 65>, 'stallion' ] 
• hashB: [ <00 6f>, hashD ] 
• hashD: [ <>, <>, <>, <>, <>, <>, hashE, <>, <>, <>, <>, <>, <>, <>, <>, <>, 'verb' ] 
• hashE: [ <17>, hashF ] 
• hashF: [ <>, <>, <>, <>, <>, <>, hashG, <>, <>, <>, <>, <>, <>, <>, <>, <>, 'puppy' ] 
• hashG: [ <35>, 'coin' ]

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章