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

54
EMPTY_TRIE_ROOT = Root(
55
    hex_to_bytes(
56
        "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"
57
    )
58
)

Node

60
Node = Union[Account, Bytes, LegacyTransaction, Receipt, Uint, U256, None]

K

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

V

62
V = TypeVar(
63
    "V",
64
    Optional[Account],
65
    Optional[Bytes],
66
    Bytes,
67
    Optional[Union[LegacyTransaction, Bytes]],
68
    Optional[Union[Receipt, Bytes]],
69
    Uint,
70
    U256,
71
)

LeafNode

Leaf node in the Merkle Trie

74
@slotted_freezable
75
@dataclass
class LeafNode:

rest_of_key

79
    rest_of_key: Bytes

value

80
    value: rlp.Extended

ExtensionNode

Extension node in the Merkle Trie

83
@slotted_freezable
84
@dataclass
class ExtensionNode:

key_segment

88
    key_segment: Bytes

subnode

89
    subnode: rlp.Extended

BranchNode

Branch node in the Merkle Trie

92
@slotted_freezable
93
@dataclass
class BranchNode:

subnodes

97
    subnodes: List[rlp.Extended]

value

98
    value: rlp.Extended

InternalNode

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

def encode_internal_node(node: Optional[InternalNode]) -> ethereum.rlp.Extended:
105
    """
106
    Encodes a Merkle Trie node into its RLP form. The RLP will then be
107
    serialized into a `Bytes` and hashed unless it is less that 32 bytes
108
    when serialized.
109
110
    This function also accepts `None`, representing the absence of a node,
111
    which is encoded to `b""`.
112
113
    Parameters
114
    ----------
115
    node : Optional[InternalNode]
116
        The node to encode.
117
118
    Returns
119
    -------
120
    encoded : `rlp.RLP`
121
        The node encoded as RLP.
122
    """
123
    unencoded: rlp.Extended
124
    if node is None:
125
        unencoded = b""
126
    elif isinstance(node, LeafNode):
127
        unencoded = (
128
            nibble_list_to_compact(node.rest_of_key, True),
129
            node.value,
130
        )
131
    elif isinstance(node, ExtensionNode):
132
        unencoded = (
133
            nibble_list_to_compact(node.key_segment, False),
134
            node.subnode,
135
        )
136
    elif isinstance(node, BranchNode):
137
        unencoded = node.subnodes + [node.value]
138
    else:
139
        raise AssertionError(f"Invalid internal node type {type(node)}!")
140
141
    encoded = rlp.encode(unencoded)
142
    if len(encoded) < 32:
143
        return unencoded
144
    else:
145
        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:
149
    """
150
    Encode a Node for storage in the Merkle Trie.
151
152
    Currently mostly an unimplemented stub.
153
    """
154
    if isinstance(node, Account):
155
        assert storage_root is not None
156
        return encode_account(node, storage_root)
157
    elif isinstance(node, (LegacyTransaction, Receipt, U256)):
158
        return rlp.encode(node)
159
    elif isinstance(node, Bytes):
160
        return node
161
    else:
162
        return previous_trie.encode_node(node, storage_root)

Trie

The Merkle Trie.

165
@dataclass
class Trie:

secured

171
    secured: bool

default

172
    default: V

_data

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