ethereum.shanghai.trieethereum.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

57
EMPTY_TRIE_ROOT = Root(
58
    hex_to_bytes(
59
        "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"
60
    )
61
)

Node

63
Node = Union[
64
    Account, Bytes, LegacyTransaction, Receipt, Uint, U256, Withdrawal, None
65
]

K

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

V

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

LeafNode

Leaf node in the Merkle Trie

80
@slotted_freezable
81
@dataclass
class LeafNode:

rest_of_key

85
    rest_of_key: Bytes

value

86
    value: rlp.Extended

ExtensionNode

Extension node in the Merkle Trie

89
@slotted_freezable
90
@dataclass
class ExtensionNode:

key_segment

94
    key_segment: Bytes

subnode

95
    subnode: rlp.Extended

BranchNode

Branch node in the Merkle Trie

98
@slotted_freezable
99
@dataclass
class BranchNode:

subnodes

103
    subnodes: List[rlp.Extended]

value

104
    value: rlp.Extended

InternalNode

107
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.Extended:
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.Extended`
127
        The node encoded as RLP.
128
    """
129
    unencoded: rlp.Extended
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)

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:
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, (LegacyTransaction, Receipt, Withdrawal, U256)):
164
        return rlp.encode(node)
165
    elif isinstance(node, Bytes):
166
        return node
167
    else:
168
        return previous_trie.encode_node(node, storage_root)
168
        return previous_trie.encode_node(node, storage_root)

Trie

The Merkle Trie.

171
@dataclass
class Trie:

secured

177
    secured: bool

default

178
    default: V

_data

179
    _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]:
183
    """
184
    Create a copy of `trie`. Since only frozen objects may be stored in tries,
185
    the contents are reused.
186
187
    Parameters
188
    ----------
189
    trie: `Trie`
190
        Trie to copy.
191
192
    Returns
193
    -------
194
    new_trie : `Trie[K, V]`
195
        A copy of the trie.
196
    """
197
    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:
201
    """
202
    Stores an item in a Merkle Trie.
203
204
    This method deletes the key if `value == trie.default`, because the Merkle
205
    Trie represents the default value by omitting it from the trie.
206
207
    Parameters
208
    ----------
209
    trie: `Trie`
210
        Trie to store in.
211
    key : `Bytes`
212
        Key to lookup.
213
    value : `V`
214
        Node to insert at `key`.
215
    """
216
    if value == trie.default:
217
        if key in trie._data:
218
            del trie._data[key]
219
    else:
220
        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:
224
    """
225
    Gets an item from the Merkle Trie.
226
227
    This method returns `trie.default` if the key is missing.
228
229
    Parameters
230
    ----------
231
    trie:
232
        Trie to lookup in.
233
    key :
234
        Key to lookup.
235
236
    Returns
237
    -------
238
    node : `V`
239
        Node at `key` in the trie.
240
    """
241
    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:
245
    """
246
    Find the longest common prefix of two sequences.
247
    """
248
    for i in range(len(a)):
249
        if i >= len(b) or a[i] != b[i]:
250
            return i
251
    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:
255
    """
256
    Compresses nibble-list into a standard byte array with a flag.
257
258
    A nibble-list is a list of byte values no greater than `15`. The flag is
259
    encoded in high nibble of the highest byte. The flag nibble can be broken
260
    down into two two-bit flags.
261
262
    Highest nibble::
263
264
        +---+---+----------+--------+
265
        | _ | _ | is_leaf | parity |
266
        +---+---+----------+--------+
267
          3   2      1         0
268
269
270
    The lowest bit of the nibble encodes the parity of the length of the
271
    remaining nibbles -- `0` when even and `1` when odd. The second lowest bit
272
    is used to distinguish leaf and extension nodes. The other two bits are not
273
    used.
274
275
    Parameters
276
    ----------
277
    x :
278
        Array of nibbles.
279
    is_leaf :
280
        True if this is part of a leaf node, or false if it is an extension
281
        node.
282
283
    Returns
284
    -------
285
    compressed : `bytearray`
286
        Compact byte array.
287
    """
288
    compact = bytearray()
289
290
    if len(x) % 2 == 0:  # ie even length
291
        compact.append(16 * (2 * is_leaf))
292
        for i in range(0, len(x), 2):
293
            compact.append(16 * x[i] + x[i + 1])
294
    else:
295
        compact.append(16 * ((2 * is_leaf) + 1) + x[0])
296
        for i in range(1, len(x), 2):
297
            compact.append(16 * x[i] + x[i + 1])
298
299
    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:
303
    """
304
    Converts a `Bytes` into to a sequence of nibbles (bytes with value < 16).
305
306
    Parameters
307
    ----------
308
    bytes_:
309
        The `Bytes` to convert.
310
311
    Returns
312
    -------
313
    nibble_list : `Bytes`
314
        The `Bytes` in nibble-list format.
315
    """
316
    nibble_list = bytearray(2 * len(bytes_))
317
    for byte_index, byte in enumerate(bytes_):
318
        nibble_list[byte_index * 2] = (byte & 0xF0) >> 4
319
        nibble_list[byte_index * 2 + 1] = byte & 0x0F
320
    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]:
327
    """
328
    Prepares the trie for root calculation. Removes values that are empty,
329
    hashes the keys (if `secured == True`) and encodes all the nodes.
330
331
    Parameters
332
    ----------
333
    trie :
334
        The `Trie` to prepare.
335
    get_storage_root :
336
        Function to get the storage root of an account. Needed to encode
337
        `Account` objects.
338
339
    Returns
340
    -------
341
    out : `Mapping[ethereum.base_types.Bytes, Node]`
342
        Object with keys mapped to nibble-byte form.
343
    """
344
    mapped: MutableMapping[Bytes, Bytes] = {}
345
346
    for preimage, value in trie._data.items():
347
        if isinstance(value, Account):
348
            assert get_storage_root is not None
349
            address = Address(preimage)
350
            encoded_value = encode_node(value, get_storage_root(address))
351
        else:
352
            encoded_value = encode_node(value)
353
        if encoded_value == b"":
354
            raise AssertionError
355
        key: Bytes
356
        if trie.secured:
357
            # "secure" tries hash keys once before construction
358
            key = keccak256(preimage)
359
        else:
360
            key = preimage
361
        mapped[bytes_to_nibble_list(key)] = encoded_value
362
363
    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:
370
    """
371
    Computes the root of a modified merkle patricia trie (MPT).
372
373
    Parameters
374
    ----------
375
    trie :
376
        `Trie` to get the root of.
377
    get_storage_root :
378
        Function to get the storage root of an account. Needed to encode
379
        `Account` objects.
380
381
382
    Returns
383
    -------
384
    root : `.fork_types.Root`
385
        MPT root of the underlying key-value pairs.
386
    """
387
    obj = _prepare_trie(trie, get_storage_root)
388
389
    root_node = encode_internal_node(patricialize(obj, Uint(0)))
390
    if len(rlp.encode(root_node)) < 32:
391
        return keccak256(rlp.encode(root_node))
392
    else:
393
        assert isinstance(root_node, Bytes)
394
        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]:
400
    """
401
    Structural composition function.
402
403
    Used to recursively patricialize and merkleize a dictionary. Includes
404
    memoization of the tree structure and hashes.
405
406
    Parameters
407
    ----------
408
    obj :
409
        Underlying trie key-value pairs, with keys in nibble-list format.
410
    level :
411
        Current trie level.
412
413
    Returns
414
    -------
415
    node : `ethereum.base_types.Bytes`
416
        Root node of `obj`.
417
    """
418
    if len(obj) == 0:
419
        return None
420
421
    arbitrary_key = next(iter(obj))
422
423
    # if leaf node
424
    if len(obj) == 1:
425
        leaf = LeafNode(arbitrary_key[level:], obj[arbitrary_key])
426
        return leaf
427
428
    # prepare for extension node check by finding max j such that all keys in
429
    # obj have the same key[i:j]
430
    substring = arbitrary_key[level:]
431
    prefix_length = len(substring)
432
    for key in obj:
433
        prefix_length = min(
434
            prefix_length, common_prefix_length(substring, key[level:])
435
        )
436
437
        # finished searching, found another key at the current level
438
        if prefix_length == 0:
439
            break
440
441
    # if extension node
442
    if prefix_length > 0:
443
        prefix = arbitrary_key[int(level) : int(level) + prefix_length]
444
        return ExtensionNode(
445
            prefix,
446
            encode_internal_node(
447
                patricialize(obj, level + Uint(prefix_length))
448
            ),
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 + Uint(1)))
467
            for k in range(16)
468
        ],
469
        value,
470
    )