ethereum.merkle_patricia_trie

State Trie.

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

Introduction

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

EMPTY_TRIE_ROOT

63
EMPTY_TRIE_ROOT = Hash32(
64
    hex_to_bytes(
65
        "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"
66
    )
67
)

LeafNode

Leaf node in the Merkle Trie.

70
@slotted_freezable
71
@dataclass
class LeafNode:

rest_of_key

75
    rest_of_key: Bytes

value

76
    value: Extended

ExtensionNode

Extension node in the Merkle Trie.

79
@slotted_freezable
80
@dataclass
class ExtensionNode:

key_segment

84
    key_segment: Bytes

subnode

85
    subnode: Extended

BranchSubnodes

88
BranchSubnodes = Tuple[
89
    Extended,
90
    Extended,
91
    Extended,
92
    Extended,
93
    Extended,
94
    Extended,
95
    Extended,
96
    Extended,
97
    Extended,
98
    Extended,
99
    Extended,
100
    Extended,
101
    Extended,
102
    Extended,
103
    Extended,
104
    Extended,
105
]

BranchNode

Branch node in the Merkle Trie.

108
@slotted_freezable
109
@dataclass
class BranchNode:

subnodes

113
    subnodes: BranchSubnodes

value

114
    value: Extended

InternalNode

117
InternalNode = LeafNode | ExtensionNode | BranchNode

K

120
K = TypeVar("K", bound=Bytes)

V

121
V = TypeVar("V", bound=Extended | None)

encode_account

Encode Account dataclass.

Storage is not stored in the Account dataclass, so Accounts cannot be encoded without providing a storage root.

def encode_account(raw_account_data: Account, ​​storage_root: Bytes) -> Bytes:
125
    """
126
    Encode `Account` dataclass.
127
128
    Storage is not stored in the `Account` dataclass, so `Accounts` cannot be
129
    encoded without providing a storage root.
130
    """
131
    return rlp.encode(
132
        (
133
            raw_account_data.nonce,
134
            raw_account_data.balance,
135
            storage_root,
136
            raw_account_data.code_hash,
137
        )
138
    )

encode_internal_node

Encodes a Merkle Trie node into its RLP form. The RLP will then be serialized into a Bytes object and hashed unless it is less than 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 : Extended The node encoded as RLP.

def encode_internal_node(node: Optional[InternalNode]) -> Extended:
142
    """
143
    Encodes a Merkle Trie node into its RLP form. The RLP will then be
144
    serialized into a `Bytes` object and hashed unless it is less than 32 bytes
145
    when serialized.
146
147
    This function also accepts `None`, representing the absence of a node,
148
    which is encoded to `b""`.
149
150
    Parameters
151
    ----------
152
    node : Optional[InternalNode]
153
        The node to encode.
154
155
    Returns
156
    -------
157
    encoded : `Extended`
158
        The node encoded as RLP.
159
160
    """
161
    unencoded: Extended
162
    if node is None:
163
        unencoded = b""
164
    elif isinstance(node, LeafNode):
165
        unencoded = (
166
            nibble_list_to_compact(node.rest_of_key, True),
167
            node.value,
168
        )
169
    elif isinstance(node, ExtensionNode):
170
        unencoded = (
171
            nibble_list_to_compact(node.key_segment, False),
172
            node.subnode,
173
        )
174
    elif isinstance(node, BranchNode):
175
        unencoded = list(node.subnodes) + [node.value]
176
    else:
177
        raise AssertionError(f"Invalid internal node type {type(node)}!")
178
179
    encoded = rlp.encode(unencoded)
180
    if len(encoded) < 32:
181
        return unencoded
182
    else:
183
        return keccak256(encoded)

encode_node

Encode a Node for storage in the Merkle Trie.

def encode_node(node: Extended, ​​storage_root: Bytes | None) -> Bytes:
187
    """
188
    Encode a Node for storage in the Merkle Trie.
189
    """
190
    from ethereum.state import Account
191
192
    if isinstance(node, Account):
193
        assert storage_root is not None
194
        return encode_account(node, storage_root)
195
    elif isinstance(node, Bytes):
196
        return node
197
    else:
198
        return rlp.encode(node)

Trie

The Merkle Trie.

201
@dataclass
class Trie:

secured

207
    secured: bool

default

208
    default: V

_data

209
    _data: Dict[K, V] = field(default_factory=dict)

copy_trie

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 copy_trie(trie: Trie[K, V]) -> Trie[K, V]:
213
    """
214
    Create a copy of `trie`. Since only frozen objects may be stored in tries,
215
    the contents are reused.
216
217
    Parameters
218
    ----------
219
    trie: `Trie`
220
        Trie to copy.
221
222
    Returns
223
    -------
224
    new_trie : `Trie[K, V]`
225
        A copy of the trie.
226
227
    """
228
    return Trie(trie.secured, trie.default, copy.copy(trie._data))

trie_set

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_set(trie: Trie[K, V], ​​key: K, ​​value: V) -> None:
232
    """
233
    Stores an item in a Merkle Trie.
234
235
    This method deletes the key if `value == trie.default`, because the Merkle
236
    Trie represents the default value by omitting it from the trie.
237
238
    Parameters
239
    ----------
240
    trie: `Trie`
241
        Trie to store in.
242
    key : `Bytes`
243
        Key to lookup.
244
    value : `V`
245
        Node to insert at `key`.
246
247
    """
248
    if value == trie.default:
249
        if key in trie._data:
250
            del trie._data[key]
251
    else:
252
        trie._data[key] = value

trie_get

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 trie_get(trie: Trie[K, V], ​​key: K) -> V:
256
    """
257
    Gets an item from the Merkle Trie.
258
259
    This method returns `trie.default` if the key is missing.
260
261
    Parameters
262
    ----------
263
    trie:
264
        Trie to lookup in.
265
    key :
266
        Key to lookup.
267
268
    Returns
269
    -------
270
    node : `V`
271
        Node at `key` in the trie.
272
273
    """
274
    return trie._data.get(key, trie.default)

common_prefix_length

Find the longest common prefix of two sequences.

def common_prefix_length(a: Sequence, ​​b: Sequence) -> int:
278
    """
279
    Find the longest common prefix of two sequences.
280
    """
281
    for i in range(len(a)):
282
        if i >= len(b) or a[i] != b[i]:
283
            return i
284
    return len(a)

nibble_list_to_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 nibble_list_to_compact(x: Bytes, ​​is_leaf: bool) -> Bytes:
288
    """
289
    Compresses nibble-list into a standard byte array with a flag.
290
291
    A nibble-list is a list of byte values no greater than `15`. The flag is
292
    encoded in high nibble of the highest byte. The flag nibble can be broken
293
    down into two two-bit flags.
294
295
    Highest nibble::
296
297
        +---+---+----------+--------+
298
        | _ | _ | is_leaf | parity |
299
        +---+---+----------+--------+
300
          3   2      1         0
301
302
303
    The lowest bit of the nibble encodes the parity of the length of the
304
    remaining nibbles -- `0` when even and `1` when odd. The second lowest bit
305
    is used to distinguish leaf and extension nodes. The other two bits are not
306
    used.
307
308
    Parameters
309
    ----------
310
    x :
311
        Array of nibbles.
312
    is_leaf :
313
        True if this is part of a leaf node, or false if it is an extension
314
        node.
315
316
    Returns
317
    -------
318
    compressed : `bytearray`
319
        Compact byte array.
320
321
    """
322
    compact = bytearray()
323
324
    if len(x) % 2 == 0:  # ie even length
325
        compact.append(16 * (2 * is_leaf))
326
        for i in range(0, len(x), 2):
327
            compact.append(16 * x[i] + x[i + 1])
328
    else:
329
        compact.append(16 * ((2 * is_leaf) + 1) + x[0])
330
        for i in range(1, len(x), 2):
331
            compact.append(16 * x[i] + x[i + 1])
332
333
    return Bytes(compact)

bytes_to_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 bytes_to_nibble_list(bytes_: Bytes) -> Bytes:
337
    """
338
    Converts a `Bytes` into to a sequence of nibbles (bytes with value < 16).
339
340
    Parameters
341
    ----------
342
    bytes_:
343
        The `Bytes` to convert.
344
345
    Returns
346
    -------
347
    nibble_list : `Bytes`
348
        The `Bytes` in nibble-list format.
349
350
    """
351
    nibble_list = bytearray(2 * len(bytes_))
352
    for byte_index, byte in enumerate(bytes_):
353
        nibble_list[byte_index * 2] = (byte & 0xF0) >> 4
354
        nibble_list[byte_index * 2 + 1] = byte & 0x0F
355
    return Bytes(nibble_list)

_prepare_trie

Prepares the trie for root calculation. Removes values that are empty, hashes the keys (if secured == True) and encodes all the nodes.

Parameters

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

Returns

out : Mapping[ethereum.base_types.Bytes, Node] Object with keys mapped to nibble-byte form.

def _prepare_trie(trie: Trie[K, V], ​​get_storage_root: Optional[Callable[[Address], Root]]) -> Mapping[Bytes, Bytes]:
362
    """
363
    Prepares the trie for root calculation. Removes values that are empty,
364
    hashes the keys (if `secured == True`) and encodes all the nodes.
365
366
    Parameters
367
    ----------
368
    trie :
369
        The `Trie` to prepare.
370
    get_storage_root :
371
        Function to get the storage root of an account. Needed to encode
372
        `Account` objects.
373
374
    Returns
375
    -------
376
    out : `Mapping[ethereum.base_types.Bytes, Node]`
377
        Object with keys mapped to nibble-byte form.
378
379
    """
380
    from ethereum.state import Account, Address
381
382
    mapped: MutableMapping[Bytes, Bytes] = {}
383
384
    for preimage, value in trie._data.items():
385
        if isinstance(value, Account):
386
            assert get_storage_root is not None
387
            address = Address(preimage)
388
            encoded_value = encode_node(value, get_storage_root(address))
389
        elif value is None:
390
            raise AssertionError("cannot encode `None`")
391
        else:
392
            encoded_value = encode_node(value)
393
        if encoded_value == b"":
394
            raise AssertionError
395
        key: Bytes
396
        if trie.secured:
397
            # "secure" tries hash keys once before construction
398
            key = keccak256(preimage)
399
        else:
400
            key = preimage
401
        mapped[bytes_to_nibble_list(key)] = encoded_value
402
403
    return mapped

root

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 : .state.Root MPT root of the underlying key-value pairs.

def root(trie: Trie[K, V], ​​get_storage_root: Optional[Callable[[Address], Root]]) -> Root:
410
    """
411
    Computes the root of a modified merkle patricia trie (MPT).
412
413
    Parameters
414
    ----------
415
    trie :
416
        `Trie` to get the root of.
417
    get_storage_root :
418
        Function to get the storage root of an account. Needed to encode
419
        `Account` objects.
420
421
422
    Returns
423
    -------
424
    root : `.state.Root`
425
        MPT root of the underlying key-value pairs.
426
427
    """
428
    from ethereum.state import Root
429
430
    obj = _prepare_trie(trie, get_storage_root)
431
432
    root_node = encode_internal_node(patricialize(obj, Uint(0)))
433
    if len(rlp.encode(root_node)) < 32:
434
        return keccak256(rlp.encode(root_node))
435
    else:
436
        assert isinstance(root_node, Bytes)
437
        return Root(root_node)

patricialize

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.

def patricialize(obj: Mapping[Bytes, Bytes], ​​level: Uint) -> Optional[InternalNode]:
443
    """
444
    Structural composition function.
445
446
    Used to recursively patricialize and merkleize a dictionary. Includes
447
    memoization of the tree structure and hashes.
448
449
    Parameters
450
    ----------
451
    obj :
452
        Underlying trie key-value pairs, with keys in nibble-list format.
453
    level :
454
        Current trie level.
455
456
    Returns
457
    -------
458
    node : `ethereum.base_types.Bytes`
459
        Root node of `obj`.
460
461
    """
462
    if len(obj) == 0:
463
        return None
464
465
    arbitrary_key = next(iter(obj))
466
467
    # if leaf node
468
    if len(obj) == 1:
469
        leaf = LeafNode(arbitrary_key[level:], obj[arbitrary_key])
470
        return leaf
471
472
    # prepare for extension node check by finding max j such that all keys in
473
    # obj have the same key[i:j]
474
    substring = arbitrary_key[level:]
475
    prefix_length = len(substring)
476
    for key in obj:
477
        prefix_length = min(
478
            prefix_length, common_prefix_length(substring, key[level:])
479
        )
480
481
        # finished searching, found another key at the current level
482
        if prefix_length == 0:
483
            break
484
485
    # if extension node
486
    if prefix_length > 0:
487
        prefix = arbitrary_key[int(level) : int(level) + prefix_length]
488
        return ExtensionNode(
489
            prefix,
490
            encode_internal_node(
491
                patricialize(obj, level + Uint(prefix_length))
492
            ),
493
        )
494
495
    branches: List[MutableMapping[Bytes, Bytes]] = []
496
    for _ in range(16):
497
        branches.append({})
498
    value = b""
499
    for key in obj:
500
        if len(key) == level:
501
            value = obj[key]
502
        else:
503
            branches[key[level]][key] = obj[key]
504
505
    subnodes = tuple(
506
        encode_internal_node(patricialize(branches[k], level + Uint(1)))
507
        for k in range(16)
508
    )
509
    return BranchNode(
510
        cast(BranchSubnodes, assert_type(subnodes, Tuple[Extended, ...])),
511
        value,
512
    )