ethereum.forks.osaka.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

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

Node

65
Node = Account | Bytes | LegacyTransaction | Receipt | Uint | U256 | Withdrawal

K

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

V

67
V = TypeVar(
68
    "V",
69
    Optional[Account],
70
    Optional[Bytes],
71
    Bytes,
72
    Optional[LegacyTransaction | Bytes],
73
    Optional[Receipt | Bytes],
74
    Optional[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: Extended

ExtensionNode

Extension node in the Merkle Trie.

89
@slotted_freezable
90
@dataclass
class ExtensionNode:

key_segment

94
    key_segment: Bytes

subnode

95
    subnode: Extended

BranchSubnodes

98
BranchSubnodes = Tuple[
99
    Extended,
100
    Extended,
101
    Extended,
102
    Extended,
103
    Extended,
104
    Extended,
105
    Extended,
106
    Extended,
107
    Extended,
108
    Extended,
109
    Extended,
110
    Extended,
111
    Extended,
112
    Extended,
113
    Extended,
114
    Extended,
115
]

BranchNode

Branch node in the Merkle Trie.

118
@slotted_freezable
119
@dataclass
class BranchNode:

subnodes

123
    subnodes: BranchSubnodes

value

124
    value: Extended

InternalNode

127
InternalNode = 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 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:
131
    """
132
    Encodes a Merkle Trie node into its RLP form. The RLP will then be
133
    serialized into a `Bytes` and hashed unless it is less than 32 bytes
134
    when serialized.
135
136
    This function also accepts `None`, representing the absence of a node,
137
    which is encoded to `b""`.
138
139
    Parameters
140
    ----------
141
    node : Optional[InternalNode]
142
        The node to encode.
143
144
    Returns
145
    -------
146
    encoded : `Extended`
147
        The node encoded as RLP.
148
149
    """
150
    unencoded: Extended
151
    if node is None:
152
        unencoded = b""
153
    elif isinstance(node, LeafNode):
154
        unencoded = (
155
            nibble_list_to_compact(node.rest_of_key, True),
156
            node.value,
157
        )
158
    elif isinstance(node, ExtensionNode):
159
        unencoded = (
160
            nibble_list_to_compact(node.key_segment, False),
161
            node.subnode,
162
        )
163
    elif isinstance(node, BranchNode):
164
        unencoded = list(node.subnodes) + [node.value]
165
    else:
166
        raise AssertionError(f"Invalid internal node type {type(node)}!")
167
168
    encoded = rlp.encode(unencoded)
169
    if len(encoded) < 32:
170
        return unencoded
171
    else:
172
        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:
176
    """
177
    Encode a Node for storage in the Merkle Trie.
178
179
    Currently mostly an unimplemented stub.
180
    """
181
    if isinstance(node, Account):
182
        assert storage_root is not None
183
        return encode_account(node, storage_root)
184
    elif isinstance(
185
        node, (LegacyTransaction, Receipt, Withdrawal, U256, Uint)
186
    ):
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
    """
221
    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:
225
    """
226
    Stores an item in a Merkle Trie.
227
228
    This method deletes the key if `value == trie.default`, because the Merkle
229
    Trie represents the default value by omitting it from the trie.
230
231
    Parameters
232
    ----------
233
    trie: `Trie`
234
        Trie to store in.
235
    key : `Bytes`
236
        Key to lookup.
237
    value : `V`
238
        Node to insert at `key`.
239
240
    """
241
    if value == trie.default:
242
        if key in trie._data:
243
            del trie._data[key]
244
    else:
245
        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:
249
    """
250
    Gets an item from the Merkle Trie.
251
252
    This method returns `trie.default` if the key is missing.
253
254
    Parameters
255
    ----------
256
    trie:
257
        Trie to lookup in.
258
    key :
259
        Key to lookup.
260
261
    Returns
262
    -------
263
    node : `V`
264
        Node at `key` in the trie.
265
266
    """
267
    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:
271
    """
272
    Find the longest common prefix of two sequences.
273
    """
274
    for i in range(len(a)):
275
        if i >= len(b) or a[i] != b[i]:
276
            return i
277
    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:
281
    """
282
    Compresses nibble-list into a standard byte array with a flag.
283
284
    A nibble-list is a list of byte values no greater than `15`. The flag is
285
    encoded in high nibble of the highest byte. The flag nibble can be broken
286
    down into two two-bit flags.
287
288
    Highest nibble::
289
290
        +---+---+----------+--------+
291
        | _ | _ | is_leaf | parity |
292
        +---+---+----------+--------+
293
          3   2      1         0
294
295
296
    The lowest bit of the nibble encodes the parity of the length of the
297
    remaining nibbles -- `0` when even and `1` when odd. The second lowest bit
298
    is used to distinguish leaf and extension nodes. The other two bits are not
299
    used.
300
301
    Parameters
302
    ----------
303
    x :
304
        Array of nibbles.
305
    is_leaf :
306
        True if this is part of a leaf node, or false if it is an extension
307
        node.
308
309
    Returns
310
    -------
311
    compressed : `bytearray`
312
        Compact byte array.
313
314
    """
315
    compact = bytearray()
316
317
    if len(x) % 2 == 0:  # ie even length
318
        compact.append(16 * (2 * is_leaf))
319
        for i in range(0, len(x), 2):
320
            compact.append(16 * x[i] + x[i + 1])
321
    else:
322
        compact.append(16 * ((2 * is_leaf) + 1) + x[0])
323
        for i in range(1, len(x), 2):
324
            compact.append(16 * x[i] + x[i + 1])
325
326
    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:
330
    """
331
    Converts a `Bytes` into to a sequence of nibbles (bytes with value < 16).
332
333
    Parameters
334
    ----------
335
    bytes_:
336
        The `Bytes` to convert.
337
338
    Returns
339
    -------
340
    nibble_list : `Bytes`
341
        The `Bytes` in nibble-list format.
342
343
    """
344
    nibble_list = bytearray(2 * len(bytes_))
345
    for byte_index, byte in enumerate(bytes_):
346
        nibble_list[byte_index * 2] = (byte & 0xF0) >> 4
347
        nibble_list[byte_index * 2 + 1] = byte & 0x0F
348
    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]:
355
    """
356
    Prepares the trie for root calculation. Removes values that are empty,
357
    hashes the keys (if `secured == True`) and encodes all the nodes.
358
359
    Parameters
360
    ----------
361
    trie :
362
        The `Trie` to prepare.
363
    get_storage_root :
364
        Function to get the storage root of an account. Needed to encode
365
        `Account` objects.
366
367
    Returns
368
    -------
369
    out : `Mapping[ethereum.base_types.Bytes, Node]`
370
        Object with keys mapped to nibble-byte form.
371
372
    """
373
    mapped: MutableMapping[Bytes, Bytes] = {}
374
375
    for preimage, value in trie._data.items():
376
        if isinstance(value, Account):
377
            assert get_storage_root is not None
378
            address = Address(preimage)
379
            encoded_value = encode_node(value, get_storage_root(address))
380
        elif value is None:
381
            raise AssertionError("cannot encode `None`")
382
        else:
383
            encoded_value = encode_node(value)
384
        if encoded_value == b"":
385
            raise AssertionError
386
        key: Bytes
387
        if trie.secured:
388
            # "secure" tries hash keys once before construction
389
            key = keccak256(preimage)
390
        else:
391
            key = preimage
392
        mapped[bytes_to_nibble_list(key)] = encoded_value
393
394
    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:
401
    """
402
    Computes the root of a modified merkle patricia trie (MPT).
403
404
    Parameters
405
    ----------
406
    trie :
407
        `Trie` to get the root of.
408
    get_storage_root :
409
        Function to get the storage root of an account. Needed to encode
410
        `Account` objects.
411
412
413
    Returns
414
    -------
415
    root : `.fork_types.Root`
416
        MPT root of the underlying key-value pairs.
417
418
    """
419
    obj = _prepare_trie(trie, get_storage_root)
420
421
    root_node = encode_internal_node(patricialize(obj, Uint(0)))
422
    if len(rlp.encode(root_node)) < 32:
423
        return keccak256(rlp.encode(root_node))
424
    else:
425
        assert isinstance(root_node, Bytes)
426
        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]:
432
    """
433
    Structural composition function.
434
435
    Used to recursively patricialize and merkleize a dictionary. Includes
436
    memoization of the tree structure and hashes.
437
438
    Parameters
439
    ----------
440
    obj :
441
        Underlying trie key-value pairs, with keys in nibble-list format.
442
    level :
443
        Current trie level.
444
445
    Returns
446
    -------
447
    node : `ethereum.base_types.Bytes`
448
        Root node of `obj`.
449
450
    """
451
    if len(obj) == 0:
452
        return None
453
454
    arbitrary_key = next(iter(obj))
455
456
    # if leaf node
457
    if len(obj) == 1:
458
        leaf = LeafNode(arbitrary_key[level:], obj[arbitrary_key])
459
        return leaf
460
461
    # prepare for extension node check by finding max j such that all keys in
462
    # obj have the same key[i:j]
463
    substring = arbitrary_key[level:]
464
    prefix_length = len(substring)
465
    for key in obj:
466
        prefix_length = min(
467
            prefix_length, common_prefix_length(substring, key[level:])
468
        )
469
470
        # finished searching, found another key at the current level
471
        if prefix_length == 0:
472
            break
473
474
    # if extension node
475
    if prefix_length > 0:
476
        prefix = arbitrary_key[int(level) : int(level) + prefix_length]
477
        return ExtensionNode(
478
            prefix,
479
            encode_internal_node(
480
                patricialize(obj, level + Uint(prefix_length))
481
            ),
482
        )
483
484
    branches: List[MutableMapping[Bytes, Bytes]] = []
485
    for _ in range(16):
486
        branches.append({})
487
    value = b""
488
    for key in obj:
489
        if len(key) == level:
490
            # shouldn't ever have an account or receipt in an internal node
491
            if isinstance(obj[key], (Account, Receipt, Uint)):
492
                raise AssertionError
493
            value = obj[key]
494
        else:
495
            branches[key[level]][key] = obj[key]
496
497
    subnodes = tuple(
498
        encode_internal_node(patricialize(branches[k], level + Uint(1)))
499
        for k in range(16)
500
    )
501
    return BranchNode(
502
        cast(BranchSubnodes, assert_type(subnodes, Tuple[Extended, ...])),
503
        value,
504
    )