ethereum.frontier.trie

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

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

Introduction

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

Leaf node in the Merkle Trie

@slotted_freezable
@dataclass
class ExtensionNode:
90@slotted_freezable
91@dataclass
92class ExtensionNode:
93    """Extension node in the Merkle Trie"""
94
95    key_segment: Bytes
96    subnode: rlp.RLP

Extension node in the Merkle Trie

@slotted_freezable
@dataclass
class BranchNode:
 99@slotted_freezable
100@dataclass
101class BranchNode:
102    """Branch node in the Merkle Trie"""
103
104    subnodes: List[rlp.RLP]
105    value: rlp.RLP

Branch node in the Merkle Trie

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

def encode_node( node: Union[ethereum.frontier.fork_types.Account, bytes, ethereum.frontier.fork_types.Transaction, ethereum.frontier.fork_types.Receipt, ethereum.base_types.Uint, ethereum.base_types.U256, NoneType], storage_root: Optional[bytes] = None) -> bytes:
155def encode_node(node: Node, storage_root: Optional[Bytes] = None) -> Bytes:
156    """
157    Encode a Node for storage in the Merkle Trie.
158
159    Currently mostly an unimplemented stub.
160    """
161    if isinstance(node, Account):
162        assert storage_root is not None
163        return encode_account(node, storage_root)
164    elif isinstance(node, (Transaction, Receipt, U256)):
165        return rlp.encode(cast(rlp.RLP, node))
166    elif isinstance(node, Bytes):
167        return node
168    else:
169        raise AssertionError(
170            f"encoding for {type(node)} is not currently implemented"
171        )

Encode a Node for storage in the Merkle Trie.

Currently mostly an unimplemented stub.

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

The Merkle Trie.

def copy_trie( trie: Trie[~K, ~V]) -> Trie[~K, ~V]:
185def copy_trie(trie: Trie[K, V]) -> Trie[K, V]:
186    """
187    Create a copy of `trie`. Since only frozen objects may be stored in tries,
188    the contents are reused.
189
190    Parameters
191    ----------
192    trie: `Trie`
193        Trie to copy.
194
195    Returns
196    -------
197    new_trie : `Trie[K, V]`
198        A copy of the trie.
199    """
200    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:
203def trie_set(trie: Trie[K, V], key: K, value: V) -> None:
204    """
205    Stores an item in a Merkle Trie.
206
207    This method deletes the key if `value == trie.default`, because the Merkle
208    Trie represents the default value by omitting it from the trie.
209
210    Parameters
211    ----------
212    trie: `Trie`
213        Trie to store in.
214    key : `Bytes`
215        Key to lookup.
216    value : `V`
217        Node to insert at `key`.
218    """
219    if value == trie.default:
220        if key in trie._data:
221            del trie._data[key]
222    else:
223        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:
226def trie_get(trie: Trie[K, V], key: K) -> V:
227    """
228    Gets an item from the Merkle Trie.
229
230    This method returns `trie.default` if the key is missing.
231
232    Parameters
233    ----------
234    trie:
235        Trie to lookup in.
236    key :
237        Key to lookup.
238
239    Returns
240    -------
241    node : `V`
242        Node at `key` in the trie.
243    """
244    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:
247def common_prefix_length(a: Sequence, b: Sequence) -> int:
248    """
249    Find the longest common prefix of two sequences.
250    """
251    for i in range(len(a)):
252        if i >= len(b) or a[i] != b[i]:
253            return i
254    return len(a)

Find the longest common prefix of two sequences.

def nibble_list_to_compact(x: bytes, is_leaf: bool) -> bytes:
257def nibble_list_to_compact(x: Bytes, is_leaf: bool) -> Bytes:
258    """
259    Compresses nibble-list into a standard byte array with a flag.
260
261    A nibble-list is a list of byte values no greater than `15`. The flag is
262    encoded in high nibble of the highest byte. The flag nibble can be broken
263    down into two two-bit flags.
264
265    Highest nibble::
266
267        +---+---+----------+--------+
268        | _ | _ | is_leaf | parity |
269        +---+---+----------+--------+
270          3   2      1         0
271
272
273    The lowest bit of the nibble encodes the parity of the length of the
274    remaining nibbles -- `0` when even and `1` when odd. The second lowest bit
275    is used to distinguish leaf and extension nodes. The other two bits are not
276    used.
277
278    Parameters
279    ----------
280    x :
281        Array of nibbles.
282    is_leaf :
283        True if this is part of a leaf node, or false if it is an extension
284        node.
285
286    Returns
287    -------
288    compressed : `bytearray`
289        Compact byte array.
290    """
291    compact = bytearray()
292
293    if len(x) % 2 == 0:  # ie even length
294        compact.append(16 * (2 * is_leaf))
295        for i in range(0, len(x), 2):
296            compact.append(16 * x[i] + x[i + 1])
297    else:
298        compact.append(16 * ((2 * is_leaf) + 1) + x[0])
299        for i in range(1, len(x), 2):
300            compact.append(16 * x[i] + x[i + 1])
301
302    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:
305def bytes_to_nibble_list(bytes_: Bytes) -> Bytes:
306    """
307    Converts a `Bytes` into to a sequence of nibbles (bytes with value < 16).
308
309    Parameters
310    ----------
311    bytes_:
312        The `Bytes` to convert.
313
314    Returns
315    -------
316    nibble_list : `Bytes`
317        The `Bytes` in nibble-list format.
318    """
319    nibble_list = bytearray(2 * len(bytes_))
320    for byte_index, byte in enumerate(bytes_):
321        nibble_list[byte_index * 2] = (byte & 0xF0) >> 4
322        nibble_list[byte_index * 2 + 1] = byte & 0x0F
323    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:
369def root(
370    trie: Trie[K, V],
371    get_storage_root: Optional[Callable[[Address], Root]] = None,
372) -> Root:
373    """
374    Computes the root of a modified merkle patricia trie (MPT).
375
376    Parameters
377    ----------
378    trie :
379        `Trie` to get the root of.
380    get_storage_root :
381        Function to get the storage root of an account. Needed to encode
382        `Account` objects.
383
384
385    Returns
386    -------
387    root : `.fork_types.Root`
388        MPT root of the underlying key-value pairs.
389    """
390    obj = _prepare_trie(trie, get_storage_root)
391
392    root_node = encode_internal_node(patricialize(obj, Uint(0)))
393    if len(rlp.encode(root_node)) < 32:
394        return keccak256(rlp.encode(root_node))
395    else:
396        assert isinstance(root_node, Bytes)
397        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]:
400def patricialize(
401    obj: Mapping[Bytes, Bytes], level: Uint
402) -> Optional[InternalNode]:
403    """
404    Structural composition function.
405
406    Used to recursively patricialize and merkleize a dictionary. Includes
407    memoization of the tree structure and hashes.
408
409    Parameters
410    ----------
411    obj :
412        Underlying trie key-value pairs, with keys in nibble-list format.
413    level :
414        Current trie level.
415
416    Returns
417    -------
418    node : `ethereum.base_types.Bytes`
419        Root node of `obj`.
420    """
421    if len(obj) == 0:
422        return None
423
424    arbitrary_key = next(iter(obj))
425
426    # if leaf node
427    if len(obj) == 1:
428        leaf = LeafNode(arbitrary_key[level:], obj[arbitrary_key])
429        return leaf
430
431    # prepare for extension node check by finding max j such that all keys in
432    # obj have the same key[i:j]
433    substring = arbitrary_key[level:]
434    prefix_length = len(substring)
435    for key in obj:
436        prefix_length = min(
437            prefix_length, common_prefix_length(substring, key[level:])
438        )
439
440        # finished searching, found another key at the current level
441        if prefix_length == 0:
442            break
443
444    # if extension node
445    if prefix_length > 0:
446        prefix = arbitrary_key[level : level + prefix_length]
447        return ExtensionNode(
448            prefix,
449            encode_internal_node(patricialize(obj, level + prefix_length)),
450        )
451
452    branches: List[MutableMapping[Bytes, Bytes]] = []
453    for _ in range(16):
454        branches.append({})
455    value = b""
456    for key in obj:
457        if len(key) == level:
458            # shouldn't ever have an account or receipt in an internal node
459            if isinstance(obj[key], (Account, Receipt, Uint)):
460                raise AssertionError
461            value = obj[key]
462        else:
463            branches[key[level]][key] = obj[key]
464
465    return BranchNode(
466        [
467            encode_internal_node(patricialize(branches[k], level + 1))
468            for k in range(16)
469        ],
470        value,
471    )

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.