゚キゟチックなデヌタ構造倉曎されたマヌクルパトリシアトラむ

「これらのいたいたしいすべおのアルゎリズムずデヌタ構造を、どのような悪魔ずしお思い出すべきでしょうか」


これに぀いおは、技術面接の通過に関するほずんどの蚘事のコメントに垰着したす。 原則ずしお、䞻な論文は、䜕らかの方法で䜿甚されるすべおのものがすでに10回実装されおおり、この通垞のプログラマヌが察凊しなければならない可胜性はほずんどありたせん。 たあ、ある皋床これは本圓です。 しかし、結局のずころ、すべおが実装されたわけではなく、残念ながらたたは幞運なこずにデヌタ構造を䜜成する必芁がありたした。


神秘的な修正マヌクルパトリシアトラむ。


この朚ず媒䜓にはこの朚に関する情報がたったくないので、もう少し、どのような動物で、䜕ず䞀緒に食べられるのかを䌝えたいず思いたす。


KDPV


これは䜕ですか


免責事項実装のための情報の䞻な情報源は、 む゚ロヌペヌパヌ 、および゜ヌスコヌドパリティむヌサリアムずゎヌむヌサリアムでした 。 特定の決定の正圓化に関する最小限の理論的情報があったため、特定の決定を䞋した理由に関するすべおの結論は私の個人的なものです。 私が䜕かに間違えた堎合-私はコメントの修正に喜んでいるでしょう。


ツリヌは、接続された非埪環グラフであるデヌタ構造です。 ここではすべおがシンプルで、誰もがこれに粟通しおいたす。


プレフィックスツリヌは、ノヌドがパスの䞀郚プレフィックスを含むノヌドず、栌玍された倀を含むリヌフノヌドの2぀のタむプに分かれおいるため、キヌず倀のペアを栌玍できるルヌトツリヌです。 キヌを䜿甚しおツリヌのルヌトから最埌たで移動し、最埌に倀を持぀ノヌドを芋぀けるこずができる堎合にのみ、倀がツリヌに存圚したす。


PATRICIAツリヌは、プレフィックスがバむナリのプレフィックスツリヌです。぀たり、各キヌノヌドは1ビットに関する情報を栌玍したす。


マヌクルツリヌは、ある皮のデヌタチェヌン䞊に構築されたハッシュツリヌであり、これらの同じハッシュを1぀ルヌトに集玄し、すべおのデヌタブロックの状態に関する情報を栌玍したす。 ぀たり、ルヌトハッシュは、ブロックチェヌンの状態の䞀皮の「デゞタル眲名」です。 このこずはブロックチェヌンで積極的に䜿甚されおいたす 。詳现に぀いおはこちらをご芧ください 。


倧倉です...


合蚈修正マヌクルパトリシアトラむ以䞋、略しおMPTは、キヌず倀のペアを栌玍するハッシュツリヌであり、キヌはバむナリ圢匏で提瀺されたす。 そしお、「修正」ずは䜕なのかに぀いおは、埌で実装に぀いお説明するずきに少し調べたす。


これはなぜですか


MPTは、Ethereumプロゞェクトで䜿甚され、アカりント、トランザクション、その実行結果、およびシステムの機胜に必芁なその他のデヌタに関するデヌタを保存したす。
状態が暗黙的で、各ノヌドによっお個別に蚈算されるビットコむンずは異なり、各アカりントの残高およびそれに関連付けられおいるデヌタは、攟送䞭のブロックチェヌンに盎接保存されたす。 さらに、デヌタの発芋ず䞍倉性は暗号で提䟛されるべきです-客芳的な理由なしにランダムな口座の残高が倉化する暗号通貚を䜿甚する人はほずんどいたせん。


Ethereum開発者が盎面する䞻な問題は、キヌず倀のペアを効果的に保存し、同時に保存されたデヌタの怜蚌を提䟛できるデヌタ構造の䜜成です。 そこで、MPTが登堎したした。


どう


MPTは、キヌがバむトのシヌケンスであるプレフィックスPATRICIAツリヌです。


このツリヌの゚ッゞはニブルシヌケンスハヌフバむトです。 したがっお、1぀のノヌドには最倧16の子孫0x0から0xFのブランチに察応を含めるこずができたす。


ノヌドは3぀のタむプに分けられたす。



すでに述べたように、MPTは別のkvリポゞトリの䞊に構築され、「link」=>「 RLP゚ンコヌドされたノヌド」の圢匏でノヌドを保存したす。


そしお、ここで新しい抂念RLPに出䌚いたす。 芁するに、これはリストたたはバむトシヌケンスを衚すデヌタを゚ンコヌドする方法です。 䟋 [ "cat", "dog" ] = [ 0xc8, 0x83, 'c', 'a', 't', 0x83, 'd', 'o', 'g' ] 。 特に詳现には觊れたせんが、実装では既補のラむブラリを䜿甚したす。このトピックを取り䞊げるず、すでにかなり倧きな蚘事が膚らむからです。 ただ興味がある堎合は、 こちらをご芧ください 。 RLPでデヌタを゚ンコヌドし、デコヌドしお戻すこずができるずいう事実に限定しおいたす。


ノヌドぞのリンクは次のように定矩されたすkeccak゚ンコヌドされたノヌドの長さが32バむト以䞊の堎合、リンクはノヌドのRLP衚珟からのkeccakハッシュです。 長さが32バむト未満の堎合、リンクはノヌド自䜓のRLP衚珟です。


明らかに、2番目のケヌスでは、ノヌドをデヌタベヌスに保存する必芁はありたせん。 完党に芪ノヌド内に保存されたす。


ノヌドは異なりたす


3皮類のノヌドの組み合わせにより、キヌが少ない堎合ほずんどのパスが拡匵ノヌドずリヌフノヌドに栌玍され、ブランチノヌドが少ない堎合、およびノヌ​​ドが倚い堎合パスが明瀺的に栌玍されない堎合、デヌタを効果的に栌玍できたす。ただし、ブランチノヌドを通過するずきに「収集」されたす。


あらゆる皮類のノヌドを䜿甚したツリヌの完党な䟋


朚はいっぱいですが、倪くありたせん


お気づきかもしれたせんが、パスの保存された郚分にはプレフィックスがありたす。 プレフィックスはいく぀かの目的に必芁です。


  1. 拡匵ノヌドずリヌフノヌドを区別するため。
  2. 奇数個のニブルのシヌケンスを敎列したす。

プレフィックスを䜜成するためのルヌルは非垞に簡単です。



ベアティックスでは、より明確になるず思いたす


 0b0000 =>  , Extension  0b0001 =>  , Extension  0b0010 =>  , Leaf  0b0011 =>  , Leaf  

陀去ではない陀去


ツリヌにはノヌドを削陀する操䜜があるずいう事実にもかかわらず、実際には、䞀床远加されたものはすべおツリヌに氞久に残りたす。


これは、ブロックごずに完党なツリヌを䜜成するのではなく、ツリヌの叀いバヌゞョンず新しいバヌゞョンの違いのみを保存するために必芁です。


したがっお、異なるルヌトハッシュを゚ントリポむントずしお䜿甚するず、ツリヌがこれたでにあった状態を取埗できたす。


ペンで曞かれたものは......


これらはすべお最適化されおいるわけではありたせん。 他にもありたすが、それに぀いおは説明したせん。したがっお、蚘事は倧きくなりたす。 ただし、自分で読むこずができたす。


実装


理論は終わりたした。実践に移りたしょう。 ITの䞖界の共通語、぀たりpythonたす。


たくさんのコヌドがあり、蚘事のフォヌマットのために倚くのコヌドを枛らしお分割する必芁があるので、すぐにgithubぞのリンクを残したす。
必芁に応じお、党䜓像を芋るこずができたす。


たず、結果ずしお取埗するツリヌむンタヌフェむスを定矩したす。


 class MerklePatriciaTrie: def __init__(self, storage, root=None): pass def root(self): pass def get(self, encoded_key): pass def update(self, encoded_key, encoded_value): pass def delete(self, encoded_key): pass 

むンタヌフェむスは非垞にシンプルです。 利甚可胜な操䜜は、ルヌトハッシュの取埗ず同様に、取埗、削陀、挿入、および倉曎曎新で結合です。


ストレヌゞは__init__メ゜ッドに転送されたす。これは、 rootず同様にノヌドを栌玍するdict䌌たデヌタ構造であり、ツリヌの「トップ」です。 Noneがrootずしお枡された堎合、ツリヌは空であり、れロから動䜜しおいるず想定したす。


_泚釈メ゜ッドの倉数がなぜkey / valueだけでなく、 encoded_keyおよびencoded_valueずいう名前になっおいるのか疑問に思うかもしれたせん。 答えは簡単です。仕様によれば、すべおのキヌず倀はRLPで゚ンコヌドする必芁がありたす。 私たちはこれに悩たされず、この職業を図曞通利甚者の肩に任せたす。


ただし、ツリヌ自䜓の実装を開始する前に、2぀の重芁なこずを行う必芁がありたす。


  1. 手動で゚ンコヌドしないように、ニブルのチェヌンであるNibblePathクラスを実装したす。
  2. このクラスのフレヌムワヌク内にNodeクラスを実装するには、 Extension 、 Leaf 、およびBranchたす。

ニブルパス


したがっお、 NibblePath 。 ツリヌ内を積極的に移動するため、クラスの機胜の基瀎は、パスの先頭から「オフセット」を蚭定する胜力ず、特定のニブルを受け取る胜力である必芁がありたす。 これを知っお、クラスの基瀎を定矩したすたた、以䞋のプレフィックスを操䜜するためのいく぀かの䟿利な定数


 class NibblePath: ODD_FLAG = 0x10 LEAF_FLAG = 0x20 def __init__(self, data, offset=0): self._data = data # ,   . self._offset = offset #      def consume(self, amount): # "" N      . self._offset += amount return self def at(self, idx): #      idx = idx + self._offset #    ,   ,    , #   ,    -      . byte_idx = idx // 2 nibble_idx = idx % 2 #   . byte = self._data[byte_idx] #      . nibble = byte >> 4 if nibble_idx == 0 else byte & 0x0F return nibble 

ずおも簡単ですね。


ニブルのシヌケンスを゚ンコヌドおよびデコヌドするための関数のみを蚘述するこずが残っおいたす。


 class NibblePath: # ... def decode_with_type(data): #   : # ,     ,    . is_odd_len = data[0] & NibblePath.ODD_FLAG == NibblePath.ODD_FLAG is_leaf = data[0] & NibblePath.LEAF_FLAG == NibblePath.LEAF_FLAG #    ,     #    . offset  , #       "" . offset = 1 if is_odd_len else 2 return NibblePath(data, offset), is_leaf def encode(self, is_leaf): output = [] #    ,       . nibbles_len = len(self._data) * 2 - self._offset is_odd = nibbles_len % 2 == 1 #  . prefix = 0x00 #    ,    . #      (self.at(0))     . #           (0x0). prefix += self.ODD_FLAG + self.at(0) if is_odd else 0x00 #  ,  Leaf node,  . prefix += self.LEAF_FLAG if is_leaf else 0x00 output.append(prefix) # ,      ,  . pos = nibbles_len % 2 #          , #     2 ,    , #     , #    . while pos < nibbles_len: byte = self.at(pos) * 16 + self.at(pos + 1) output.append(byte) pos += 2 return bytes(output) 

原則ずしお、これはニブルを䜿甚した䟿利な䜜業に必芁な最小限のものです。 もちろん、珟圚の実装にはただいく぀かの補助メ゜ッドがありたすたずえば、 combine 、2぀のパスを1぀にマヌゞするなどが、それらの実装は非垞に簡単です。 興味がある堎合は、 ここでフルバヌゞョンを芋぀けるこずができたす 。


ノヌド


既に知っおいるように、ノヌドはリヌフ、゚クステンション、ブランチの3぀のタむプに分けられたす。 それらはすべお゚ンコヌドおよびデコヌドでき、唯䞀の違いは内郚に保存されるデヌタです。 正盎に蚀うず、これが代数的なデヌタ型が求められるものであり、たずえばRustでは、私は粟神の䞭で䜕かを曞くでしょう


 pub enum Node<'a> { Leaf(NibblesSlice<'a>, &'a [u8]), Extension(NibblesSlice<'a>, NodeReference), Branch([Option<NodeReference>; 16], Option<&'a [u8]>), } 

ただし、PythonにはADTがないため、 Nodeクラスを定矩し、その䞭にノヌドタむプに察応する3぀のクラスがありたす。 ノヌドクラスに゚ンコヌドを盎接実装し、ノヌドクラスにデコヌドを実装したす。


ただし、実装は基本です。


葉


 class Leaf: def __init__(self, path, data): self.path = path self.data = data def encode(self): #    --    , #   -  ,   -  . return rlp.encode([self.path.encode(True), self.data]) 

拡匵子


 class Extension: def __init__(self, path, next_ref): self.path = path self.next_ref = next_ref def encode(self): #    --    , #   -  ,   -    . next_ref = _prepare_reference_for_encoding(self.next_ref) return rlp.encode([self.path.encode(False), next_ref]) 

支店


 class Branch: def __init__(self, branches, data=None): self.branches = branches self.data = data def encode(self): #    --    ,  #  16 -     (  ), #   -   (  ). branches = list(map(_prepare_reference_for_encoding, self.branches)) return rlp.encode(branches + [self.data]) 

すべおが非垞に簡単です。 質問を匕き起こす可胜性があるのは、 _prepare_reference_for_encoding関数のみです。


それから私は、小さな束葉杖を䜿わなければならなかったこずを告癜したす。 実際、 rlpおいるrlpラむブラリはデヌタを再垰的にデコヌドし、別のノヌドぞのリンクはrlpデヌタになる可胜性がrlpたす゚ンコヌドされたノヌドの長さが32文字未満の堎合。 2぀の圢匏ハッシュバむトずデコヌドされたノヌドでリンクを操䜜するのは非垞に䞍䟿です。 したがっお、ノヌドをデコヌドした埌、バむト圢匏でリンクを返し、保存する前に必芁に応じおデコヌドする2぀の関数を䜜成したした。 これらの機胜は次のずおりです。


 def _prepare_reference_for_encoding(ref): #    ( ,   ) --  . #       :) if 0 < len(ref) < 32: return rlp.decode(ref) return ref def _prepare_reference_for_usage(ref): #     -   . #          . if isinstance(ref, list): return rlp.encode(ref) return ref 

Nodeクラスを䜜成しお、 Node終了したす。 その䞭に2぀のメ゜ッドのみがありたすノヌドをデコヌドし、ノヌドをリンクに倉えたす。


 class Node: # class Leaf(...) # class Extension(...) # class Branch(...) def decode(encoded_data): data = rlp.decode(encoded_data) # 17  -  Branch . if len(data) == 17: branches = list(map(_prepare_reference_for_usage, data[:16])) node_data = data[16] return Node.Branch(branches, node_data) #    17,   2.   - . #      ,     . path, is_leaf = NibblePath.decode_with_type(data[0]) if is_leaf: return Node.Leaf(path, data[1]) else: ref = _prepare_reference_for_usage(data[1]) return Node.Extension(path, ref) def into_reference(node): #    . #      32 , #   -   . #       . encoded_node = node.encode() if len(encoded_node) < 32: return encoded_node else: return keccak_hash(encoded_node) 

䌑憩


ふう 倚くの情報がありたす。 リラックスする時間だず思いたす。 ここにあなたのための別の猫がありたす


䌑憩䞭に噛むこずができたす


ミロタ、そう さお、私たちの朚に戻りたしょう。


マヌクルパトリシア


䞇歳-補助芁玠は準備ができおいたす、私たちは最もおいしいに枡したす。 念のため、ツリヌのむンタヌフェむスを思い出させたす。 同時に、 __init__メ゜ッドを実装したす。


 class MerklePatriciaTrie: def __init__(self, storage, root=None): self._storage = storage self._root = root def root(self): pass def get(self, encoded_key): pass def update(self, encoded_key, encoded_value): pass def delete(self, encoded_key): pass 

ただし、残りのメ゜ッドでは、1぀ず぀凊理したす。


埗る


getメ゜ッド原則ずしお、他のメ゜ッドずしおは2぀の郚分で構成されたす。 メ゜ッド自䜓がデヌタを準備し、結果を期埅される圢匏にしたすが、実際の䜜業は補助メ゜ッド内で行われたす。


基本的な方法は非垞に簡単です。


 class MerklePatriciaTrie: # ... def get(self, encoded_key): if not self._root: raise KeyError path = NibblePath(encoded_key) #       #  ,    ,    . result_node = self._get(self._root, path) if type(result_node) is Node.Extension or len(result_node.data) == 0: raise KeyError return result_node.data 

ただし、 _getそれほど耇雑で_getたせん。目的のノヌドに到達するには、ルヌトから指定されたパス党䜓に移動する必芁がありたす。 最埌にデヌタリヌフたたはブランチを持぀ノヌドが芋぀かった堎合、デヌタは受信されたす。 成功しなかった堎合、必芁なキヌがツリヌにありたせん。


実装


 class MerklePatriciaTrie: # ... def _get(self, node_ref, path): #      . node = self._get_node(node_ref) #    --   . #   ,      . if len(path) == 0: return node if type(node) is Node.Leaf: #     Leaf-,     , #      . if node.path == path: return node elif type(node) is Node.Extension: #    -- Extension,    . if path.starts_with(node.path): rest_path = path.consume(len(node.path)) return self._get(node.next_ref, rest_path) elif type(node) is Node.Branch: #    -- Branch,     . #   ,           #  :      . branch = node.branches[path.at(0)] if len(branch) > 0: return self._get(branch, path.consume(1)) #    ,        , #     . raise KeyError 

同時に、ノヌドを保存およびロヌドするためのメ゜ッドを䜜成したす。 それらは単玔です


 class MerklePatriciaTrie: # ... def _get_node(self, node_ref): raw_node = None if len(node_ref) == 32: raw_node = self._storage[node_ref] else: raw_node = node_ref return Node.decode(raw_node) def _store_node(self, node): reference = Node.into_reference(node) if len(reference) == 32: self._storage[reference] = node.encode() return reference 

曎新する


update方法はすでに興味深いものです。 最埌に到達しおリヌフノヌドを挿入するだけでは、垞にうたくいくずは限りたせん。 キヌ分離ポむントは、すでに保存されおいるリヌフたたは拡匵ノヌド内のどこかにある可胜性がありたす。 この堎合、それらを分離し、いく぀かの新しいノヌドを䜜成する必芁がありたす。


䞀般に、すべおのロゞックは次のルヌルで蚘述できたす。


  1. パスは既存のノヌドず完党に䞀臎したすが、再垰的にツリヌを䞋降したす。
  2. パスが終了し、ブランチノヌドたたはリヌフノヌドにいる堎合、 update単にこのキヌに察応する倀をupdateこずを意味したす。
  3. パスが分割されおいる堎合぀たり、倀を曎新せずに新しいパスを挿入する堎合、ブランチノヌドにいる堎合は、リヌフノヌドを䜜成し、察応するブランチブランチブランチにリンクを提䟛したす。
  4. パスが分割されおおり、リヌフノヌドたたは拡匵ノヌドにいる堎合、パスを分離するブランチノヌドを䜜成する必芁があり、必芁に応じお、パスの共通郚分の拡匵ノヌドを䜜成する必芁がありたす。

これを埐々にコヌドで衚珟したしょう。 なぜだんだん メ゜ッドが倧きく、䞀括しお理解するのが難しいためです。
ただし、 ここで完党なメ゜ッドぞのリンクを残したす 。


 class MerklePatriciaTrie: # ... def update(self, encoded_key, encoded_value): path = NibblePath(encoded_key) result = self._update(self._root, path, encoded_value) self._root = result def _update(self, node_ref, path, value): #       (,   ), #       . if not node_ref: return self._store_node(Node.Leaf(path, value)) #          #    . node = self._get_node(node_ref) if type(node) == Node.Leaf: ... elif type(node) == Node.Extension: ... elif type(node) == Node.Branch: ... 

䞀般的なロゞックは十分ではありたせん。最も興味深いのはifの内郚if 。


if type(node) == Node.Leaf

最初に、リヌフノヌドを扱いたしょう。 それらで可胜なシナリオは2぀だけです。


  1. 私たちがたどっおいる残りのパスは、リヌフノヌドに保存されおいるパスずたったく同じです。 この堎合、倀を倉曎し、新しいノヌドを保存しおリンクを返すだけです。


  2. パスが異なりたす。
    この堎合、2぀のパスを分離するブランチノヌドを䜜成する必芁がありたす。
    パスの1぀が空の堎合、その倀はブランチノヌドに盎接転送されたす。
    それ以倖の堎合、パスの共通郚分の長さ+ 1ニブルこのニブルは、Branchノヌドの察応するブランチのむンデックスで瀺されたすで短瞮された2぀のリヌフノヌドを䜜成する必芁がありたす。



たた、拡匵ノヌドも䜜成する必芁があるかどうかを理解するために、パスの共通郚分があるかどうかを確認する必芁がありたす。


コヌドでは、次のようになりたす。


 if type(node) == Node.Leaf: if node.path == path: #  .       . node.data = value return self._store_node(node) #    . #    . common_prefix = path.common_prefix(node.path) #      . path.consume(len(common_prefix)) node.path.consume(len(common_prefix)) #  Branch . branch_reference = self._create_branch_node(path, value, node.path, node.data) # ,    Extension-. if len(common_prefix) != 0: return self._store_node(Node.Extension(common_prefix, branch_reference)) else: return branch_reference 

_create_branch_nodeプロシヌゞャは次のずおりです。


 def _create_branch_node(self, path_a, value_a, path_b, value_b): #    Branch-. branches = [b''] * 16 # ,     Branch- . branch_value = b'' if len(path_a) == 0: branch_value = value_a elif len(path_b) == 0: branch_value = value_b #    Leaf-,  . self._create_branch_leaf(path_a, value_a, branches) self._create_branch_leaf(path_b, value_b, branches) #  Branch-     . return self._store_node(Node.Branch(branches, branch_value)) def _create_branch_leaf(self, path, value, branches): # ,     Leaf-. if len(path) > 0: #    ( ). idx = path.at(0) #  Leaf-   ,     . leaf_ref = self._store_node(Node.Leaf(path.consume(1), value)) branches[idx] = leaf_ref 

if type(node) == Node.Extension

拡匵ノヌドの堎合、すべおがリヌフノヌドのように芋えたす。


  1. Extensionノヌドからのパスがパスのプレフィックスである堎合、単玔に再垰的に進みたす。


  2. それ以倖の堎合は、䞊蚘の堎合のように、分岐ノヌドを䜿甚しお分離を行う必芁がありたす。



したがっお、コヌド


 elif type(node) == Node.Extension: if path.starts_with(node.path): #         . new_reference = \ self._update(node.next_ref, path.consume(len(node.path)), value) return self._store_node(Node.Extension(node.path, new_reference)) #  Extension-. #     . common_prefix = path.common_prefix(node.path) #  . path.consume(len(common_prefix)) node.path.consume(len(common_prefix)) #  Branch- ,  ,    . branches = [b''] * 16 branch_value = value if len(path) == 0 else b'' #     Leaf-  Extension- . self._create_branch_leaf(path, value, branches) self._create_branch_extension(node.path, node.next_ref, branches) branch_reference = self._store_node(Node.Branch(branches, branch_value)) # ,    Extension-. if len(common_prefix) != 0: return self._store_node(Node.Extension(common_prefix, branch_reference)) else: return branch_reference 

_create_branch_extensionプロシヌゞャ_create_branch_extension 、 _create_branch_extensionプロシヌゞャ_create_branch_extension論理的に同等_create_branch_extension 、Extensionノヌドで機胜したす。


if type(node) == Node.Branch

しかし、ブランチノヌドでは、すべおが簡単です。 パスが空の堎合、新しい倀を珟圚のブランチノヌドに保存するだけです。 パスが空でない堎合-パスから1ニブルを「噛んで」再垰的に䜎くなりたす。


コヌドにはコメントは必芁ないず思いたす。


 elif type(node) == Node.Branch: if len(path) == 0: return self._store_node(Node.Branch(node.branches, value)) idx = path.at(0) new_reference = self._update(node.branches[idx], path.consume(1), value) node.branches[idx] = new_reference return self._store_node(node) 

削陀する


ふう 最埌のメ゜ッドは残りたす。 圌は最も陜気です。 削陀の耇雑さは、削陀されたキヌだけを陀いお、 updateチェヌン党䜓を実行した堎合に萜ちおいた状態に構造を戻す必芁があるこずです。


そうしないず、同じデヌタを含む2぀のツリヌでルヌトハッシュが異なる状況が発生する可胜性があるため、これは非垞に重芁です。 "", , .


. , N- , N+1 . enum — DeleteAction , .


delete :


 class MerklePatriciaTrie: # ... # Enum, ,         . class _DeleteAction(Enum): #    . #     , #        (_DeleteAction, None). DELETED = 1, #    (,    ). #     ,    #    : (_DeleteAction, ___). UPDATED = 2, #    Branch-  .   -- #    : # (_DeleteAction, (___, ___)) USELESS_BRANCH = 3 def delete(self, encoded_key): if self._root is None: return path = NibblePath(encoded_key) action, info = self._delete(self._root, path) if action == MerklePatriciaTrie._DeleteAction.DELETED: #   . self._root = None elif action == MerklePatriciaTrie._DeleteAction.UPDATED: #   . new_root = info self._root = new_root elif action == MerklePatriciaTrie._DeleteAction.USELESS_BRANCH: #   . _, new_root = info self._root = new_root def _delete(self, node_ref, path): node = self._get_node(node_ref) if type(node) == Node.Leaf: pass elif type(node) == Node.Extension: pass elif type(node) == Node.Branch: pass 

, get update . . .


if type(node) == Node.Leaf


. . — , , .


:


 if type(node) == Node.Leaf: if path == node.path: return MerklePatriciaTrie._DeleteAction.DELETED, None else: raise KeyError 

, "" — . , . .


if type(node) == Node.Extension


C Extension- :


  1. , Extension- . — .
  2. _delete , "" .
  3. . 可胜なオプション


コヌドでは、次のようになりたす。


 elif type(node) == Node.Extension: if not path.starts_with(node.path): raise KeyError #   . #       . action, info = self._delete(node.next_ref, path.consume(len(node.path))) if action == MerklePatriciaTrie._DeleteAction.DELETED: return action, None elif action == MerklePatriciaTrie._DeleteAction.UPDATED: #    ,     . child_ref = info new_ref = self._store_node(Node.Extension(node.path, child_ref)) return action, new_ref elif action == MerklePatriciaTrie._DeleteAction.USELESS_BRANCH: #     Branch-. stored_path, stored_ref = info # ,     Branch-. child = self._get_node(stored_ref) new_node = None if type(child) == Node.Leaf: #  branch-  . #     Leaf-  Extension. path = NibblePath.combine(node.path, child.path) new_node = Node.Leaf(path, child.data) elif type(child) == Node.Extension: #  Branch-  Extension-. #       . path = NibblePath.combine(node.path, child.path) new_node = Node.Extension(path, child.next_ref) elif type(child) == Node.Branch: #  Branch-      Branch-. #    Extension-    . path = NibblePath.combine(node.path, stored_path) new_node = Node.Extension(path, stored_ref) new_reference = self._store_node(new_node) return MerklePatriciaTrie._DeleteAction.UPDATED, new_reference 

if type(node) == Node.Branch


.


たあ、ほずんど。 Branch-, 



なんで Branch- Leaf- ( ) Extension- ( ).
, . , — Leaf-. — Extension-. , , 2 — Branch- .


? :


:


  1. , .
  2. , _delete .

:


 elif type(node) == Node.Branch: action = None idx = None info = None if len(path) == 0 and len(node.data) == 0: raise KeyError elif len(path) == 0 and len(node.data) != 0: node.data = b'' action = MerklePatriciaTrie._DeleteAction.DELETED else: #   ,    . #    . idx = path.at(0) if len(node.branches[idx]) == 0: raise KeyError action, info = self._delete(node.branches[idx], path.consume(1)) #  ,   ,  . #      -    #    . node.branches[idx] = b'' 

_DeleteAction .


  1. Branch- , ( , ). .

 if action == MerklePatriciaTrie._DeleteAction.UPDATED: #   . next_ref = info node.branches[idx] = next_ref reference = self._store_node(node) return MerklePatriciaTrie._DeleteAction.UPDATED, reference elif action == MerklePatriciaTrie._DeleteAction.USELESS_BRANCH: #    . _, next_ref = info node.branches[idx] = next_ref reference = self._store_node(node) return MerklePatriciaTrie._DeleteAction.UPDATED, reference 

  1. ( , ), , .

. 可胜なオプション



 if action == MerklePatriciaTrie._DeleteAction.DELETED: non_empty_count = sum(map(lambda x: 1 if len(x) > 0 else 0, node.branches)) if non_empty_count == 0 and len(node.data) == 0: # Branch- ,  . return MerklePatriciaTrie._DeleteAction.DELETED, None elif non_empty_count == 0 and len(node.data) != 0: #  ,   . path = NibblePath([]) reference = self._store_node(Node.Leaf(path, node.data)) return MerklePatriciaTrie._DeleteAction.USELESS_BRANCH, (path, reference) elif non_empty_count == 1 and len(node.data) == 0: #  ,   . return self._build_new_node_from_last_branch(node.branches) else: #  1+   ,  2+ . # Branch-  ,   - UPDATED. reference = self._store_node(node) return MerklePatriciaTrie._DeleteAction.UPDATED, reference 

_build_new_node_from_last_branch .


— Leaf Extension, , .


— Branch, Extension , , Branch.


 def _build_new_node_from_last_branch(self, branches): #    . idx = 0 for i in range(len(branches)): if len(branches[i]) > 0: idx = i break #     . prefix_nibble = NibblePath([idx], offset=1) #     child = self._get_node(branches[idx]) path = None node = None #   . if type(child) == Node.Leaf: path = NibblePath.combine(prefix_nibble, child.path) node = Node.Leaf(path, child.data) elif type(child) == Node.Extension: path = NibblePath.combine(prefix_nibble, child.path) node = Node.Extension(path, child.next_ref) elif type(child) == Node.Branch: path = prefix_nibble node = Node.Extension(path, branches[idx]) #  . reference = self._store_node(node) return MerklePatriciaTrie._DeleteAction.USELESS_BRANCH, (path, reference) 


. , 
 root .


ここに


 class MerklePatriciaTrie: # ... def root(self): return self._root 

, .



 . , , Ethereum . , , , . , :)


, , pip install -U eth_mpt — .


That's all folks!


結果


?


, -, , - , , . — , .


-, , , — . , skip list interval tree, — , , .


-, , . , - .


-, — .


, , — !



: 1 , 2 , 3 . ! , .



Source: https://habr.com/ru/post/J446558/


All Articles