ethereum.cancun.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

60
EMPTY_TRIE_ROOT = Root(
61
    hex_to_bytes(
62
        "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"
63
    )
64
)

Node

66
Node = Union[
67
    Account, Bytes, LegacyTransaction, Receipt, Uint, U256, Withdrawal, None
68
]

K

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

V

70
V = TypeVar(
71
    "V",
72
    Optional[Account],
73
    Optional[Bytes],
74
    Bytes,
75
    Optional[Union[LegacyTransaction, Bytes]],
76
    Optional[Union[Receipt, Bytes]],
77
    Optional[Union[Withdrawal, Bytes]],
78
    Uint,
79
    U256,
80
)

LeafNode

Leaf node in the Merkle Trie

83
@slotted_freezable
84
@dataclass
class LeafNode:

rest_of_key

88
    rest_of_key: Bytes

value

89
    value: rlp.Extended

ExtensionNode

Extension node in the Merkle Trie

92
@slotted_freezable
93
@dataclass
class ExtensionNode:

key_segment

97
    key_segment: Bytes

subnode

98
    subnode: rlp.Extended

BranchSubnodes

101
BranchSubnodes = Tuple[
102
    rlp.Extended,
103
    rlp.Extended,
104
    rlp.Extended,
105
    rlp.Extended,
106
    rlp.Extended,
107
    rlp.Extended,
108
    rlp.Extended,
109
    rlp.Extended,
110
    rlp.Extended,
111
    rlp.Extended,
112
    rlp.Extended,
113
    rlp.Extended,
114
    rlp.Extended,
115
    rlp.Extended,
116
    rlp.Extended,
117
    rlp.Extended,
118
]

BranchNode

Branch node in the Merkle Trie

121
@slotted_freezable
122
@dataclass
class BranchNode:

subnodes

126
    subnodes: BranchSubnodes

value

127
    value: rlp.Extended

InternalNode

130
InternalNode = Union[LeafNode, ExtensionNode, BranchNode]

encode_internal_node

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.Extended The node encoded as RLP.

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

encode_node

Encode a Node for storage in the Merkle Trie.

Currently mostly an unimplemented stub.

def encode_node(node: Node, ​​storage_root: Optional[Bytes]) -> Bytes:
178
    """
179
    Encode a Node for storage in the Merkle Trie.
180
181
    Currently mostly an unimplemented stub.
182
    """
183
    if isinstance(node, Account):
184
        assert storage_root is not None
185
        return encode_account(node, storage_root)
186
    elif isinstance(node, (LegacyTransaction, Receipt, Withdrawal, U256)):
187
        return rlp.encode(node)
188
    elif isinstance(node, Bytes):
189
        return node
190
    else:
191
        return previous_trie.encode_node(node, storage_root)

Trie

The Merkle Trie.

194
@dataclass
class Trie:

secured

200
    secured: bool

default

201
    default: V

_data

202
    _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]:
206
    """
207
    Create a copy of `trie`. Since only frozen objects may be stored in tries,
208
    the contents are reused.
209
210
    Parameters
211
    ----------
212
    trie: `Trie`
213
        Trie to copy.
214
215
    Returns
216
    -------
217
    new_trie : `Trie[K, V]`
218
        A copy of the trie.
219
    """
220
    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:
224
    """
225
    Stores an item in a Merkle Trie.
226
227
    This method deletes the key if `value == trie.default`, because the Merkle
228
    Trie represents the default value by omitting it from the trie.
229
230
    Parameters
231
    ----------
232
    trie: `Trie`
233
        Trie to store in.
234
    key : `Bytes`
235
        Key to lookup.
236
    value : `V`
237
        Node to insert at `key`.
238
    """
239
    if value == trie.default:
240
        if key in trie._data:
241
            del trie._data[key]
242
    else:
243
        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:
247
    """
248
    Gets an item from the Merkle Trie.
249
250
    This method returns `trie.default` if the key is missing.
251
252
    Parameters
253
    ----------
254
    trie:
255
        Trie to lookup in.
256
    key :
257
        Key to lookup.
258
259
    Returns
260
    -------
261
    node : `V`
262
        Node at `key` in the trie.
263
    """
264
    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:
268
    """
269
    Find the longest common prefix of two sequences.
270
    """
271
    for i in range(len(a)):
272
        if i >= len(b) or a[i] != b[i]:
273
            return i
274
    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:
278
    """
279
    Compresses nibble-list into a standard byte array with a flag.
280
281
    A nibble-list is a list of byte values no greater than `15`. The flag is
282
    encoded in high nibble of the highest byte. The flag nibble can be broken
283
    down into two two-bit flags.
284
285
    Highest nibble::
286
287
        +---+---+----------+--------+
288
        | _ | _ | is_leaf | parity |
289
        +---+---+----------+--------+
290
          3   2      1         0
291
292
293
    The lowest bit of the nibble encodes the parity of the length of the
294
    remaining nibbles -- `0` when even and `1` when odd. The second lowest bit
295
    is used to distinguish leaf and extension nodes. The other two bits are not
296
    used.
297
298
    Parameters
299
    ----------
300
    x :
301
        Array of nibbles.
302
    is_leaf :
303
        True if this is part of a leaf node, or false if it is an extension
304
        node.
305
306
    Returns
307
    -------
308
    compressed : `bytearray`
309
        Compact byte array.
310
    """
311
    compact = bytearray()
312
313
    if len(x) % 2 == 0:  # ie even length
314
        compact.append(16 * (2 * is_leaf))
315
        for i in range(0, len(x), 2):
316
            compact.append(16 * x[i] + x[i + 1])
317
    else:
318
        compact.append(16 * ((2 * is_leaf) + 1) + x[0])
319
        for i in range(1, len(x), 2):
320
            compact.append(16 * x[i] + x[i + 1])
321
322
    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:
326
    """
327
    Converts a `Bytes` into to a sequence of nibbles (bytes with value < 16).
328
329
    Parameters
330
    ----------
331
    bytes_:
332
        The `Bytes` to convert.
333
334
    Returns
335
    -------
336
    nibble_list : `Bytes`
337
        The `Bytes` in nibble-list format.
338
    """
339
    nibble_list = bytearray(2 * len(bytes_))
340
    for byte_index, byte in enumerate(bytes_):
341
        nibble_list[byte_index * 2] = (byte & 0xF0) >> 4
342
        nibble_list[byte_index * 2 + 1] = byte & 0x0F
343
    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]:
350
    """
351
    Prepares the trie for root calculation. Removes values that are empty,
352
    hashes the keys (if `secured == True`) and encodes all the nodes.
353
354
    Parameters
355
    ----------
356
    trie :
357
        The `Trie` to prepare.
358
    get_storage_root :
359
        Function to get the storage root of an account. Needed to encode
360
        `Account` objects.
361
362
    Returns
363
    -------
364
    out : `Mapping[ethereum.base_types.Bytes, Node]`
365
        Object with keys mapped to nibble-byte form.
366
    """
367
    mapped: MutableMapping[Bytes, Bytes] = {}
368
369
    for preimage, value in trie._data.items():
370
        if isinstance(value, Account):
371
            assert get_storage_root is not None
372
            address = Address(preimage)
373
            encoded_value = encode_node(value, get_storage_root(address))
374
        else:
375
            encoded_value = encode_node(value)
376
        if encoded_value == b"":
377
            raise AssertionError
378
        key: Bytes
379
        if trie.secured:
380
            # "secure" tries hash keys once before construction
381
            key = keccak256(preimage)
382
        else:
383
            key = preimage
384
        mapped[bytes_to_nibble_list(key)] = encoded_value
385
386
    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 : .fork_types.Root MPT root of the underlying key-value pairs.

def root(trie: Trie[K, V], ​​get_storage_root: Optional[Callable[[Address], Root]]) -> Root:
393
    """
394
    Computes the root of a modified merkle patricia trie (MPT).
395
396
    Parameters
397
    ----------
398
    trie :
399
        `Trie` to get the root of.
400
    get_storage_root :
401
        Function to get the storage root of an account. Needed to encode
402
        `Account` objects.
403
404
405
    Returns
406
    -------
407
    root : `.fork_types.Root`
408
        MPT root of the underlying key-value pairs.
409
    """
410
    obj = _prepare_trie(trie, get_storage_root)
411
412
    root_node = encode_internal_node(patricialize(obj, Uint(0)))
413
    if len(rlp.encode(root_node)) < 32:
414
        return keccak256(rlp.encode(root_node))
415
    else:
416
        assert isinstance(root_node, Bytes)
417
        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]:
423
    """
424
    Structural composition function.
425
426
    Used to recursively patricialize and merkleize a dictionary. Includes
427
    memoization of the tree structure and hashes.
428
429
    Parameters
430
    ----------
431
    obj :
432
        Underlying trie key-value pairs, with keys in nibble-list format.
433
    level :
434
        Current trie level.
435
436
    Returns
437
    -------
438
    node : `ethereum.base_types.Bytes`
439
        Root node of `obj`.
440
    """
441
    if len(obj) == 0:
442
        return None
443
444
    arbitrary_key = next(iter(obj))
445
446
    # if leaf node
447
    if len(obj) == 1:
448
        leaf = LeafNode(arbitrary_key[level:], obj[arbitrary_key])
449
        return leaf
450
451
    # prepare for extension node check by finding max j such that all keys in
452
    # obj have the same key[i:j]
453
    substring = arbitrary_key[level:]
454
    prefix_length = len(substring)
455
    for key in obj:
456
        prefix_length = min(
457
            prefix_length, common_prefix_length(substring, key[level:])
458
        )
459
460
        # finished searching, found another key at the current level
461
        if prefix_length == 0:
462
            break
463
464
    # if extension node
465
    if prefix_length > 0:
466
        prefix = arbitrary_key[int(level) : int(level) + prefix_length]
467
        return ExtensionNode(
468
            prefix,
469
            encode_internal_node(
470
                patricialize(obj, level + Uint(prefix_length))
471
            ),
472
        )
473
474
    branches: List[MutableMapping[Bytes, Bytes]] = []
475
    for _ in range(16):
476
        branches.append({})
477
    value = b""
478
    for key in obj:
479
        if len(key) == level:
480
            # shouldn't ever have an account or receipt in an internal node
481
            if isinstance(obj[key], (Account, Receipt, Uint)):
482
                raise AssertionError
483
            value = obj[key]
484
        else:
485
            branches[key[level]][key] = obj[key]
486
487
    subnodes = tuple(
488
        encode_internal_node(patricialize(branches[k], level + Uint(1)))
489
        for k in range(16)
490
    )
491
    return BranchNode(
492
        cast(BranchSubnodes, assert_type(subnodes, Tuple[rlp.Extended, ...])),
493
        value,
494
    )