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

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

Trie

The Merkle Trie.

192
@dataclass
class Trie:

secured

198
    secured: bool

default

199
    default: V

_data

200
    _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]:
204
    """
205
    Create a copy of `trie`. Since only frozen objects may be stored in tries,
206
    the contents are reused.
207
208
    Parameters
209
    ----------
210
    trie: `Trie`
211
        Trie to copy.
212
213
    Returns
214
    -------
215
    new_trie : `Trie[K, V]`
216
        A copy of the trie.
217
218
    """
219
    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:
223
    """
224
    Stores an item in a Merkle Trie.
225
226
    This method deletes the key if `value == trie.default`, because the Merkle
227
    Trie represents the default value by omitting it from the trie.
228
229
    Parameters
230
    ----------
231
    trie: `Trie`
232
        Trie to store in.
233
    key : `Bytes`
234
        Key to lookup.
235
    value : `V`
236
        Node to insert at `key`.
237
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
    """
265
    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:
269
    """
270
    Find the longest common prefix of two sequences.
271
    """
272
    for i in range(len(a)):
273
        if i >= len(b) or a[i] != b[i]:
274
            return i
275
    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:
279
    """
280
    Compresses nibble-list into a standard byte array with a flag.
281
282
    A nibble-list is a list of byte values no greater than `15`. The flag is
283
    encoded in high nibble of the highest byte. The flag nibble can be broken
284
    down into two two-bit flags.
285
286
    Highest nibble::
287
288
        +---+---+----------+--------+
289
        | _ | _ | is_leaf | parity |
290
        +---+---+----------+--------+
291
          3   2      1         0
292
293
294
    The lowest bit of the nibble encodes the parity of the length of the
295
    remaining nibbles -- `0` when even and `1` when odd. The second lowest bit
296
    is used to distinguish leaf and extension nodes. The other two bits are not
297
    used.
298
299
    Parameters
300
    ----------
301
    x :
302
        Array of nibbles.
303
    is_leaf :
304
        True if this is part of a leaf node, or false if it is an extension
305
        node.
306
307
    Returns
308
    -------
309
    compressed : `bytearray`
310
        Compact byte array.
311
312
    """
313
    compact = bytearray()
314
315
    if len(x) % 2 == 0:  # ie even length
316
        compact.append(16 * (2 * is_leaf))
317
        for i in range(0, len(x), 2):
318
            compact.append(16 * x[i] + x[i + 1])
319
    else:
320
        compact.append(16 * ((2 * is_leaf) + 1) + x[0])
321
        for i in range(1, len(x), 2):
322
            compact.append(16 * x[i] + x[i + 1])
323
324
    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:
328
    """
329
    Converts a `Bytes` into to a sequence of nibbles (bytes with value < 16).
330
331
    Parameters
332
    ----------
333
    bytes_:
334
        The `Bytes` to convert.
335
336
    Returns
337
    -------
338
    nibble_list : `Bytes`
339
        The `Bytes` in nibble-list format.
340
341
    """
342
    nibble_list = bytearray(2 * len(bytes_))
343
    for byte_index, byte in enumerate(bytes_):
344
        nibble_list[byte_index * 2] = (byte & 0xF0) >> 4
345
        nibble_list[byte_index * 2 + 1] = byte & 0x0F
346
    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]:
353
    """
354
    Prepares the trie for root calculation. Removes values that are empty,
355
    hashes the keys (if `secured == True`) and encodes all the nodes.
356
357
    Parameters
358
    ----------
359
    trie :
360
        The `Trie` to prepare.
361
    get_storage_root :
362
        Function to get the storage root of an account. Needed to encode
363
        `Account` objects.
364
365
    Returns
366
    -------
367
    out : `Mapping[ethereum.base_types.Bytes, Node]`
368
        Object with keys mapped to nibble-byte form.
369
370
    """
371
    mapped: MutableMapping[Bytes, Bytes] = {}
372
373
    for preimage, value in trie._data.items():
374
        if isinstance(value, Account):
375
            assert get_storage_root is not None
376
            address = Address(preimage)
377
            encoded_value = encode_node(value, get_storage_root(address))
378
        elif value is None:
379
            raise AssertionError("cannot encode `None`")
380
        else:
381
            encoded_value = encode_node(value)
382
        if encoded_value == b"":
383
            raise AssertionError
384
        key: Bytes
385
        if trie.secured:
386
            # "secure" tries hash keys once before construction
387
            key = keccak256(preimage)
388
        else:
389
            key = preimage
390
        mapped[bytes_to_nibble_list(key)] = encoded_value
391
392
    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:
399
    """
400
    Computes the root of a modified merkle patricia trie (MPT).
401
402
    Parameters
403
    ----------
404
    trie :
405
        `Trie` to get the root of.
406
    get_storage_root :
407
        Function to get the storage root of an account. Needed to encode
408
        `Account` objects.
409
410
411
    Returns
412
    -------
413
    root : `.fork_types.Root`
414
        MPT root of the underlying key-value pairs.
415
416
    """
417
    obj = _prepare_trie(trie, get_storage_root)
418
419
    root_node = encode_internal_node(patricialize(obj, Uint(0)))
420
    if len(rlp.encode(root_node)) < 32:
421
        return keccak256(rlp.encode(root_node))
422
    else:
423
        assert isinstance(root_node, Bytes)
424
        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]:
430
    """
431
    Structural composition function.
432
433
    Used to recursively patricialize and merkleize a dictionary. Includes
434
    memoization of the tree structure and hashes.
435
436
    Parameters
437
    ----------
438
    obj :
439
        Underlying trie key-value pairs, with keys in nibble-list format.
440
    level :
441
        Current trie level.
442
443
    Returns
444
    -------
445
    node : `ethereum.base_types.Bytes`
446
        Root node of `obj`.
447
448
    """
449
    if len(obj) == 0:
450
        return None
451
452
    arbitrary_key = next(iter(obj))
453
454
    # if leaf node
455
    if len(obj) == 1:
456
        leaf = LeafNode(arbitrary_key[level:], obj[arbitrary_key])
457
        return leaf
458
459
    # prepare for extension node check by finding max j such that all keys in
460
    # obj have the same key[i:j]
461
    substring = arbitrary_key[level:]
462
    prefix_length = len(substring)
463
    for key in obj:
464
        prefix_length = min(
465
            prefix_length, common_prefix_length(substring, key[level:])
466
        )
467
468
        # finished searching, found another key at the current level
469
        if prefix_length == 0:
470
            break
471
472
    # if extension node
473
    if prefix_length > 0:
474
        prefix = arbitrary_key[int(level) : int(level) + prefix_length]
475
        return ExtensionNode(
476
            prefix,
477
            encode_internal_node(
478
                patricialize(obj, level + Uint(prefix_length))
479
            ),
480
        )
481
482
    branches: List[MutableMapping[Bytes, Bytes]] = []
483
    for _ in range(16):
484
        branches.append({})
485
    value = b""
486
    for key in obj:
487
        if len(key) == level:
488
            # shouldn't ever have an account or receipt in an internal node
489
            if isinstance(obj[key], (Account, Receipt, Uint)):
490
                raise AssertionError
491
            value = obj[key]
492
        else:
493
            branches[key[level]][key] = obj[key]
494
495
    subnodes = tuple(
496
        encode_internal_node(patricialize(branches[k], level + Uint(1)))
497
        for k in range(16)
498
    )
499
    return BranchNode(
500
        cast(BranchSubnodes, assert_type(subnodes, Tuple[Extended, ...])),
501
        value,
502
    )