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

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

Node

62
Node = Union[Account, Bytes, Transaction, Receipt, Uint, U256, None]

K

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

V

64
V = TypeVar(
65
    "V",
66
    Optional[Account],
67
    Optional[Bytes],
68
    Bytes,
69
    Optional[Transaction],
70
    Optional[Receipt],
71
    Uint,
72
    U256,
73
)

LeafNode

Leaf node in the Merkle Trie

76
@slotted_freezable
77
@dataclass
class LeafNode:

rest_of_key

81
    rest_of_key: Bytes

value

82
    value: rlp.Extended

ExtensionNode

Extension node in the Merkle Trie

85
@slotted_freezable
86
@dataclass
class ExtensionNode:

key_segment

90
    key_segment: Bytes

subnode

91
    subnode: rlp.Extended

BranchNode

Branch node in the Merkle Trie

94
@slotted_freezable
95
@dataclass
class BranchNode:

subnodes

99
    subnodes: List[rlp.Extended]

value

100
    value: rlp.Extended

InternalNode

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

Trie

The Merkle Trie.

169
@dataclass
class Trie:

secured

175
    secured: bool

default

176
    default: V

_data

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