ethereum.shanghai.trie

State Trie ^^^^^^^^^^

.. contents:: Table of Contents :backlinks: none :local:

Introduction

The state trie is the structure responsible for storing ethereum.shanghai.fork_types.Account objects.

  1"""
  2State Trie
  3^^^^^^^^^^
  4
  5.. contents:: Table of Contents
  6    :backlinks: none
  7    :local:
  8
  9Introduction
 10------------
 11
 12The state trie is the structure responsible for storing
 13`.fork_types.Account` objects.
 14"""
 15
 16import copy
 17from dataclasses import dataclass, field
 18from typing import (
 19    Callable,
 20    Dict,
 21    Generic,
 22    List,
 23    Mapping,
 24    MutableMapping,
 25    Optional,
 26    Sequence,
 27    TypeVar,
 28    Union,
 29    cast,
 30)
 31
 32from ethereum.crypto.hash import keccak256
 33from ethereum.paris import trie as previous_trie
 34from ethereum.utils.ensure import ensure
 35from ethereum.utils.hexadecimal import hex_to_bytes
 36
 37from .. import rlp
 38from ..base_types import U256, Bytes, Uint, slotted_freezable
 39from .fork_types import (
 40    Account,
 41    Address,
 42    LegacyTransaction,
 43    Receipt,
 44    Root,
 45    Withdrawal,
 46    encode_account,
 47)
 48
 49# note: an empty trie (regardless of whether it is secured) has root:
 50#
 51#   keccak256(RLP(b''))
 52#       ==
 53#   56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421 # noqa: E501,SC10
 54#
 55# also:
 56#
 57#   keccak256(RLP(()))
 58#       ==
 59#   1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347 # noqa: E501,SC10
 60#
 61# which is the sha3Uncles hash in block header with no uncles
 62EMPTY_TRIE_ROOT = Root(
 63    hex_to_bytes(
 64        "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"
 65    )
 66)
 67
 68Node = Union[
 69    Account, Bytes, LegacyTransaction, Receipt, Uint, U256, Withdrawal, None
 70]
 71K = TypeVar("K", bound=Bytes)
 72V = TypeVar(
 73    "V",
 74    Optional[Account],
 75    Optional[Bytes],
 76    Bytes,
 77    Optional[Union[LegacyTransaction, Bytes]],
 78    Optional[Union[Receipt, Bytes]],
 79    Optional[Union[Withdrawal, Bytes]],
 80    Uint,
 81    U256,
 82)
 83
 84
 85@slotted_freezable
 86@dataclass
 87class LeafNode:
 88    """Leaf node in the Merkle Trie"""
 89
 90    rest_of_key: Bytes
 91    value: rlp.RLP
 92
 93
 94@slotted_freezable
 95@dataclass
 96class ExtensionNode:
 97    """Extension node in the Merkle Trie"""
 98
 99    key_segment: Bytes
100    subnode: rlp.RLP
101
102
103@slotted_freezable
104@dataclass
105class BranchNode:
106    """Branch node in the Merkle Trie"""
107
108    subnodes: List[rlp.RLP]
109    value: rlp.RLP
110
111
112InternalNode = Union[LeafNode, ExtensionNode, BranchNode]
113
114
115def encode_internal_node(node: Optional[InternalNode]) -> rlp.RLP:
116    """
117    Encodes a Merkle Trie node into its RLP form. The RLP will then be
118    serialized into a `Bytes` and hashed unless it is less that 32 bytes
119    when serialized.
120
121    This function also accepts `None`, representing the absence of a node,
122    which is encoded to `b""`.
123
124    Parameters
125    ----------
126    node : Optional[InternalNode]
127        The node to encode.
128
129    Returns
130    -------
131    encoded : `rlp.RLP`
132        The node encoded as RLP.
133    """
134    unencoded: rlp.RLP
135    if node is None:
136        unencoded = b""
137    elif isinstance(node, LeafNode):
138        unencoded = (
139            nibble_list_to_compact(node.rest_of_key, True),
140            node.value,
141        )
142    elif isinstance(node, ExtensionNode):
143        unencoded = (
144            nibble_list_to_compact(node.key_segment, False),
145            node.subnode,
146        )
147    elif isinstance(node, BranchNode):
148        unencoded = node.subnodes + [node.value]
149    else:
150        raise AssertionError(f"Invalid internal node type {type(node)}!")
151
152    encoded = rlp.encode(unencoded)
153    if len(encoded) < 32:
154        return unencoded
155    else:
156        return keccak256(encoded)
157
158
159def encode_node(node: Node, storage_root: Optional[Bytes] = None) -> Bytes:
160    """
161    Encode a Node for storage in the Merkle Trie.
162
163    Currently mostly an unimplemented stub.
164    """
165    if isinstance(node, Account):
166        assert storage_root is not None
167        return encode_account(node, storage_root)
168    elif isinstance(node, (LegacyTransaction, Receipt, Withdrawal, U256)):
169        return rlp.encode(cast(rlp.RLP, node))
170    elif isinstance(node, Bytes):
171        return node
172    else:
173        return previous_trie.encode_node(node, storage_root)
174
175
176@dataclass
177class Trie(Generic[K, V]):
178    """
179    The Merkle Trie.
180    """
181
182    secured: bool
183    default: V
184    _data: Dict[K, V] = field(default_factory=dict)
185
186
187def copy_trie(trie: Trie[K, V]) -> Trie[K, V]:
188    """
189    Create a copy of `trie`. Since only frozen objects may be stored in tries,
190    the contents are reused.
191
192    Parameters
193    ----------
194    trie: `Trie`
195        Trie to copy.
196
197    Returns
198    -------
199    new_trie : `Trie[K, V]`
200        A copy of the trie.
201    """
202    return Trie(trie.secured, trie.default, copy.copy(trie._data))
203
204
205def trie_set(trie: Trie[K, V], key: K, value: V) -> None:
206    """
207    Stores an item in a Merkle Trie.
208
209    This method deletes the key if `value == trie.default`, because the Merkle
210    Trie represents the default value by omitting it from the trie.
211
212    Parameters
213    ----------
214    trie: `Trie`
215        Trie to store in.
216    key : `Bytes`
217        Key to lookup.
218    value : `V`
219        Node to insert at `key`.
220    """
221    if value == trie.default:
222        if key in trie._data:
223            del trie._data[key]
224    else:
225        trie._data[key] = value
226
227
228def trie_get(trie: Trie[K, V], key: K) -> V:
229    """
230    Gets an item from the Merkle Trie.
231
232    This method returns `trie.default` if the key is missing.
233
234    Parameters
235    ----------
236    trie:
237        Trie to lookup in.
238    key :
239        Key to lookup.
240
241    Returns
242    -------
243    node : `V`
244        Node at `key` in the trie.
245    """
246    return trie._data.get(key, trie.default)
247
248
249def common_prefix_length(a: Sequence, b: Sequence) -> int:
250    """
251    Find the longest common prefix of two sequences.
252    """
253    for i in range(len(a)):
254        if i >= len(b) or a[i] != b[i]:
255            return i
256    return len(a)
257
258
259def nibble_list_to_compact(x: Bytes, is_leaf: bool) -> Bytes:
260    """
261    Compresses nibble-list into a standard byte array with a flag.
262
263    A nibble-list is a list of byte values no greater than `15`. The flag is
264    encoded in high nibble of the highest byte. The flag nibble can be broken
265    down into two two-bit flags.
266
267    Highest nibble::
268
269        +---+---+----------+--------+
270        | _ | _ | is_leaf | parity |
271        +---+---+----------+--------+
272          3   2      1         0
273
274
275    The lowest bit of the nibble encodes the parity of the length of the
276    remaining nibbles -- `0` when even and `1` when odd. The second lowest bit
277    is used to distinguish leaf and extension nodes. The other two bits are not
278    used.
279
280    Parameters
281    ----------
282    x :
283        Array of nibbles.
284    is_leaf :
285        True if this is part of a leaf node, or false if it is an extension
286        node.
287
288    Returns
289    -------
290    compressed : `bytearray`
291        Compact byte array.
292    """
293    compact = bytearray()
294
295    if len(x) % 2 == 0:  # ie even length
296        compact.append(16 * (2 * is_leaf))
297        for i in range(0, len(x), 2):
298            compact.append(16 * x[i] + x[i + 1])
299    else:
300        compact.append(16 * ((2 * is_leaf) + 1) + x[0])
301        for i in range(1, len(x), 2):
302            compact.append(16 * x[i] + x[i + 1])
303
304    return Bytes(compact)
305
306
307def bytes_to_nibble_list(bytes_: Bytes) -> Bytes:
308    """
309    Converts a `Bytes` into to a sequence of nibbles (bytes with value < 16).
310
311    Parameters
312    ----------
313    bytes_:
314        The `Bytes` to convert.
315
316    Returns
317    -------
318    nibble_list : `Bytes`
319        The `Bytes` in nibble-list format.
320    """
321    nibble_list = bytearray(2 * len(bytes_))
322    for byte_index, byte in enumerate(bytes_):
323        nibble_list[byte_index * 2] = (byte & 0xF0) >> 4
324        nibble_list[byte_index * 2 + 1] = byte & 0x0F
325    return Bytes(nibble_list)
326
327
328def _prepare_trie(
329    trie: Trie[K, V],
330    get_storage_root: Optional[Callable[[Address], Root]] = None,
331) -> Mapping[Bytes, Bytes]:
332    """
333    Prepares the trie for root calculation. Removes values that are empty,
334    hashes the keys (if `secured == True`) and encodes all the nodes.
335
336    Parameters
337    ----------
338    trie :
339        The `Trie` to prepare.
340    get_storage_root :
341        Function to get the storage root of an account. Needed to encode
342        `Account` objects.
343
344    Returns
345    -------
346    out : `Mapping[ethereum.base_types.Bytes, Node]`
347        Object with keys mapped to nibble-byte form.
348    """
349    mapped: MutableMapping[Bytes, Bytes] = {}
350
351    for preimage, value in trie._data.items():
352        if isinstance(value, Account):
353            assert get_storage_root is not None
354            address = Address(preimage)
355            encoded_value = encode_node(value, get_storage_root(address))
356        else:
357            encoded_value = encode_node(value)
358        # Empty values are represented by their absence
359        ensure(encoded_value != b"", AssertionError)
360        key: Bytes
361        if trie.secured:
362            # "secure" tries hash keys once before construction
363            key = keccak256(preimage)
364        else:
365            key = preimage
366        mapped[bytes_to_nibble_list(key)] = encoded_value
367
368    return mapped
369
370
371def root(
372    trie: Trie[K, V],
373    get_storage_root: Optional[Callable[[Address], Root]] = None,
374) -> Root:
375    """
376    Computes the root of a modified merkle patricia trie (MPT).
377
378    Parameters
379    ----------
380    trie :
381        `Trie` to get the root of.
382    get_storage_root :
383        Function to get the storage root of an account. Needed to encode
384        `Account` objects.
385
386
387    Returns
388    -------
389    root : `.fork_types.Root`
390        MPT root of the underlying key-value pairs.
391    """
392    obj = _prepare_trie(trie, get_storage_root)
393
394    root_node = encode_internal_node(patricialize(obj, Uint(0)))
395    if len(rlp.encode(root_node)) < 32:
396        return keccak256(rlp.encode(root_node))
397    else:
398        assert isinstance(root_node, Bytes)
399        return Root(root_node)
400
401
402def patricialize(
403    obj: Mapping[Bytes, Bytes], level: Uint
404) -> Optional[InternalNode]:
405    """
406    Structural composition function.
407
408    Used to recursively patricialize and merkleize a dictionary. Includes
409    memoization of the tree structure and hashes.
410
411    Parameters
412    ----------
413    obj :
414        Underlying trie key-value pairs, with keys in nibble-list format.
415    level :
416        Current trie level.
417
418    Returns
419    -------
420    node : `ethereum.base_types.Bytes`
421        Root node of `obj`.
422    """
423    if len(obj) == 0:
424        return None
425
426    arbitrary_key = next(iter(obj))
427
428    # if leaf node
429    if len(obj) == 1:
430        leaf = LeafNode(arbitrary_key[level:], obj[arbitrary_key])
431        return leaf
432
433    # prepare for extension node check by finding max j such that all keys in
434    # obj have the same key[i:j]
435    substring = arbitrary_key[level:]
436    prefix_length = len(substring)
437    for key in obj:
438        prefix_length = min(
439            prefix_length, common_prefix_length(substring, key[level:])
440        )
441
442        # finished searching, found another key at the current level
443        if prefix_length == 0:
444            break
445
446    # if extension node
447    if prefix_length > 0:
448        prefix = arbitrary_key[level : level + prefix_length]
449        return ExtensionNode(
450            prefix,
451            encode_internal_node(patricialize(obj, level + prefix_length)),
452        )
453
454    branches: List[MutableMapping[Bytes, Bytes]] = []
455    for _ in range(16):
456        branches.append({})
457    value = b""
458    for key in obj:
459        if len(key) == level:
460            # shouldn't ever have an account or receipt in an internal node
461            if isinstance(obj[key], (Account, Receipt, Uint)):
462                raise AssertionError
463            value = obj[key]
464        else:
465            branches[key[level]][key] = obj[key]
466
467    return BranchNode(
468        [
469            encode_internal_node(patricialize(branches[k], level + 1))
470            for k in range(16)
471        ],
472        value,
473    )
@slotted_freezable
@dataclass
class LeafNode:
86@slotted_freezable
87@dataclass
88class LeafNode:
89    """Leaf node in the Merkle Trie"""
90
91    rest_of_key: Bytes
92    value: rlp.RLP

Leaf node in the Merkle Trie

@slotted_freezable
@dataclass
class ExtensionNode:
 95@slotted_freezable
 96@dataclass
 97class ExtensionNode:
 98    """Extension node in the Merkle Trie"""
 99
100    key_segment: Bytes
101    subnode: rlp.RLP

Extension node in the Merkle Trie

@slotted_freezable
@dataclass
class BranchNode:
104@slotted_freezable
105@dataclass
106class BranchNode:
107    """Branch node in the Merkle Trie"""
108
109    subnodes: List[rlp.RLP]
110    value: rlp.RLP

Branch node in the Merkle Trie

def encode_internal_node( node: Union[LeafNode, ExtensionNode, BranchNode, NoneType]) -> Any:
116def encode_internal_node(node: Optional[InternalNode]) -> rlp.RLP:
117    """
118    Encodes a Merkle Trie node into its RLP form. The RLP will then be
119    serialized into a `Bytes` and hashed unless it is less that 32 bytes
120    when serialized.
121
122    This function also accepts `None`, representing the absence of a node,
123    which is encoded to `b""`.
124
125    Parameters
126    ----------
127    node : Optional[InternalNode]
128        The node to encode.
129
130    Returns
131    -------
132    encoded : `rlp.RLP`
133        The node encoded as RLP.
134    """
135    unencoded: rlp.RLP
136    if node is None:
137        unencoded = b""
138    elif isinstance(node, LeafNode):
139        unencoded = (
140            nibble_list_to_compact(node.rest_of_key, True),
141            node.value,
142        )
143    elif isinstance(node, ExtensionNode):
144        unencoded = (
145            nibble_list_to_compact(node.key_segment, False),
146            node.subnode,
147        )
148    elif isinstance(node, BranchNode):
149        unencoded = node.subnodes + [node.value]
150    else:
151        raise AssertionError(f"Invalid internal node type {type(node)}!")
152
153    encoded = rlp.encode(unencoded)
154    if len(encoded) < 32:
155        return unencoded
156    else:
157        return keccak256(encoded)

Encodes a Merkle Trie node into its RLP form. The RLP will then be serialized into a Bytes and hashed unless it is less that 32 bytes when serialized.

This function also accepts None, representing the absence of a node, which is encoded to b"".

Parameters

node : Optional[InternalNode] The node to encode.

Returns

encoded : rlp.RLP The node encoded as RLP.

160def encode_node(node: Node, storage_root: Optional[Bytes] = None) -> Bytes:
161    """
162    Encode a Node for storage in the Merkle Trie.
163
164    Currently mostly an unimplemented stub.
165    """
166    if isinstance(node, Account):
167        assert storage_root is not None
168        return encode_account(node, storage_root)
169    elif isinstance(node, (LegacyTransaction, Receipt, Withdrawal, U256)):
170        return rlp.encode(cast(rlp.RLP, node))
171    elif isinstance(node, Bytes):
172        return node
173    else:
174        return previous_trie.encode_node(node, storage_root)

Encode a Node for storage in the Merkle Trie.

Currently mostly an unimplemented stub.

@dataclass
class Trie(typing.Generic[~K, ~V]):
177@dataclass
178class Trie(Generic[K, V]):
179    """
180    The Merkle Trie.
181    """
182
183    secured: bool
184    default: V
185    _data: Dict[K, V] = field(default_factory=dict)

The Merkle Trie.

def copy_trie( trie: Trie[~K, ~V]) -> Trie[~K, ~V]:
188def copy_trie(trie: Trie[K, V]) -> Trie[K, V]:
189    """
190    Create a copy of `trie`. Since only frozen objects may be stored in tries,
191    the contents are reused.
192
193    Parameters
194    ----------
195    trie: `Trie`
196        Trie to copy.
197
198    Returns
199    -------
200    new_trie : `Trie[K, V]`
201        A copy of the trie.
202    """
203    return Trie(trie.secured, trie.default, copy.copy(trie._data))

Create a copy of trie. Since only frozen objects may be stored in tries, the contents are reused.

Parameters

trie: Trie Trie to copy.

Returns

new_trie : Trie[K, V] A copy of the trie.

def trie_set(trie: Trie[~K, ~V], key: ~K, value: ~V) -> None:
206def trie_set(trie: Trie[K, V], key: K, value: V) -> None:
207    """
208    Stores an item in a Merkle Trie.
209
210    This method deletes the key if `value == trie.default`, because the Merkle
211    Trie represents the default value by omitting it from the trie.
212
213    Parameters
214    ----------
215    trie: `Trie`
216        Trie to store in.
217    key : `Bytes`
218        Key to lookup.
219    value : `V`
220        Node to insert at `key`.
221    """
222    if value == trie.default:
223        if key in trie._data:
224            del trie._data[key]
225    else:
226        trie._data[key] = value

Stores an item in a Merkle Trie.

This method deletes the key if value == trie.default, because the Merkle Trie represents the default value by omitting it from the trie.

Parameters

trie: Trie Trie to store in. key : Bytes Key to lookup. value : V Node to insert at key.

def trie_get(trie: Trie[~K, ~V], key: ~K) -> ~V:
229def trie_get(trie: Trie[K, V], key: K) -> V:
230    """
231    Gets an item from the Merkle Trie.
232
233    This method returns `trie.default` if the key is missing.
234
235    Parameters
236    ----------
237    trie:
238        Trie to lookup in.
239    key :
240        Key to lookup.
241
242    Returns
243    -------
244    node : `V`
245        Node at `key` in the trie.
246    """
247    return trie._data.get(key, trie.default)

Gets an item from the Merkle Trie.

This method returns trie.default if the key is missing.

Parameters

trie: Trie to lookup in. key : Key to lookup.

Returns

node : V Node at key in the trie.

def common_prefix_length(a: Sequence, b: Sequence) -> int:
250def common_prefix_length(a: Sequence, b: Sequence) -> int:
251    """
252    Find the longest common prefix of two sequences.
253    """
254    for i in range(len(a)):
255        if i >= len(b) or a[i] != b[i]:
256            return i
257    return len(a)

Find the longest common prefix of two sequences.

def nibble_list_to_compact(x: bytes, is_leaf: bool) -> bytes:
260def nibble_list_to_compact(x: Bytes, is_leaf: bool) -> Bytes:
261    """
262    Compresses nibble-list into a standard byte array with a flag.
263
264    A nibble-list is a list of byte values no greater than `15`. The flag is
265    encoded in high nibble of the highest byte. The flag nibble can be broken
266    down into two two-bit flags.
267
268    Highest nibble::
269
270        +---+---+----------+--------+
271        | _ | _ | is_leaf | parity |
272        +---+---+----------+--------+
273          3   2      1         0
274
275
276    The lowest bit of the nibble encodes the parity of the length of the
277    remaining nibbles -- `0` when even and `1` when odd. The second lowest bit
278    is used to distinguish leaf and extension nodes. The other two bits are not
279    used.
280
281    Parameters
282    ----------
283    x :
284        Array of nibbles.
285    is_leaf :
286        True if this is part of a leaf node, or false if it is an extension
287        node.
288
289    Returns
290    -------
291    compressed : `bytearray`
292        Compact byte array.
293    """
294    compact = bytearray()
295
296    if len(x) % 2 == 0:  # ie even length
297        compact.append(16 * (2 * is_leaf))
298        for i in range(0, len(x), 2):
299            compact.append(16 * x[i] + x[i + 1])
300    else:
301        compact.append(16 * ((2 * is_leaf) + 1) + x[0])
302        for i in range(1, len(x), 2):
303            compact.append(16 * x[i] + x[i + 1])
304
305    return Bytes(compact)

Compresses nibble-list into a standard byte array with a flag.

A nibble-list is a list of byte values no greater than 15. The flag is encoded in high nibble of the highest byte. The flag nibble can be broken down into two two-bit flags.

Highest nibble::

+---+---+----------+--------+
| _ | _ | is_leaf | parity |
+---+---+----------+--------+
  3   2      1         0

The lowest bit of the nibble encodes the parity of the length of the remaining nibbles -- 0 when even and 1 when odd. The second lowest bit is used to distinguish leaf and extension nodes. The other two bits are not used.

Parameters

x : Array of nibbles. is_leaf : True if this is part of a leaf node, or false if it is an extension node.

Returns

compressed : bytearray Compact byte array.

def bytes_to_nibble_list(bytes_: bytes) -> bytes:
308def bytes_to_nibble_list(bytes_: Bytes) -> Bytes:
309    """
310    Converts a `Bytes` into to a sequence of nibbles (bytes with value < 16).
311
312    Parameters
313    ----------
314    bytes_:
315        The `Bytes` to convert.
316
317    Returns
318    -------
319    nibble_list : `Bytes`
320        The `Bytes` in nibble-list format.
321    """
322    nibble_list = bytearray(2 * len(bytes_))
323    for byte_index, byte in enumerate(bytes_):
324        nibble_list[byte_index * 2] = (byte & 0xF0) >> 4
325        nibble_list[byte_index * 2 + 1] = byte & 0x0F
326    return Bytes(nibble_list)

Converts a Bytes into to a sequence of nibbles (bytes with value < 16).

Parameters

bytes_: The Bytes to convert.

Returns

nibble_list : Bytes The Bytes in nibble-list format.

def root( trie: Trie[~K, ~V], get_storage_root: Optional[Callable[[ethereum.base_types.Bytes20], ethereum.base_types.Bytes32]] = None) -> ethereum.base_types.Bytes32:
372def root(
373    trie: Trie[K, V],
374    get_storage_root: Optional[Callable[[Address], Root]] = None,
375) -> Root:
376    """
377    Computes the root of a modified merkle patricia trie (MPT).
378
379    Parameters
380    ----------
381    trie :
382        `Trie` to get the root of.
383    get_storage_root :
384        Function to get the storage root of an account. Needed to encode
385        `Account` objects.
386
387
388    Returns
389    -------
390    root : `.fork_types.Root`
391        MPT root of the underlying key-value pairs.
392    """
393    obj = _prepare_trie(trie, get_storage_root)
394
395    root_node = encode_internal_node(patricialize(obj, Uint(0)))
396    if len(rlp.encode(root_node)) < 32:
397        return keccak256(rlp.encode(root_node))
398    else:
399        assert isinstance(root_node, Bytes)
400        return Root(root_node)

Computes the root of a modified merkle patricia trie (MPT).

Parameters

trie : Trie to get the root of. get_storage_root : Function to get the storage root of an account. Needed to encode Account objects.

Returns

root : .fork_types.Root MPT root of the underlying key-value pairs.

def patricialize( obj: Mapping[bytes, bytes], level: ethereum.base_types.Uint) -> Union[LeafNode, ExtensionNode, BranchNode, NoneType]:
403def patricialize(
404    obj: Mapping[Bytes, Bytes], level: Uint
405) -> Optional[InternalNode]:
406    """
407    Structural composition function.
408
409    Used to recursively patricialize and merkleize a dictionary. Includes
410    memoization of the tree structure and hashes.
411
412    Parameters
413    ----------
414    obj :
415        Underlying trie key-value pairs, with keys in nibble-list format.
416    level :
417        Current trie level.
418
419    Returns
420    -------
421    node : `ethereum.base_types.Bytes`
422        Root node of `obj`.
423    """
424    if len(obj) == 0:
425        return None
426
427    arbitrary_key = next(iter(obj))
428
429    # if leaf node
430    if len(obj) == 1:
431        leaf = LeafNode(arbitrary_key[level:], obj[arbitrary_key])
432        return leaf
433
434    # prepare for extension node check by finding max j such that all keys in
435    # obj have the same key[i:j]
436    substring = arbitrary_key[level:]
437    prefix_length = len(substring)
438    for key in obj:
439        prefix_length = min(
440            prefix_length, common_prefix_length(substring, key[level:])
441        )
442
443        # finished searching, found another key at the current level
444        if prefix_length == 0:
445            break
446
447    # if extension node
448    if prefix_length > 0:
449        prefix = arbitrary_key[level : level + prefix_length]
450        return ExtensionNode(
451            prefix,
452            encode_internal_node(patricialize(obj, level + prefix_length)),
453        )
454
455    branches: List[MutableMapping[Bytes, Bytes]] = []
456    for _ in range(16):
457        branches.append({})
458    value = b""
459    for key in obj:
460        if len(key) == level:
461            # shouldn't ever have an account or receipt in an internal node
462            if isinstance(obj[key], (Account, Receipt, Uint)):
463                raise AssertionError
464            value = obj[key]
465        else:
466            branches[key[level]][key] = obj[key]
467
468    return BranchNode(
469        [
470            encode_internal_node(patricialize(branches[k], level + 1))
471            for k in range(16)
472        ],
473        value,
474    )

Structural composition function.

Used to recursively patricialize and merkleize a dictionary. Includes memoization of the tree structure and hashes.

Parameters

obj : Underlying trie key-value pairs, with keys in nibble-list format. level : Current trie level.

Returns

node : ethereum.base_types.Bytes Root node of obj.