ethereum.state_mpt

Merkle-Patricia-Trie-backed implementation of the shared state model.

The State class here is the in-memory implementation of the PreState protocol used on Ethereum mainnet: accounts and storage live in Merkle Patricia Tries and the state root is the MPT commitment. Other providers, such as databases, witnesses, or other commitment schemes, are separate implementations of PreState.

State

Contains all information that is preserved between transactions.

32
@final
33
@dataclass
class State:

_main_trie

39
    _main_trie: Trie[Address, Optional[Account]] = field(
40
        default_factory=lambda: Trie(secured=True, default=None)
41
    )

_storage_tries

42
    _storage_tries: Dict[Address, Trie[Bytes32, U256]] = field(
43
        default_factory=dict
44
    )

_code_store

45
    _code_store: Dict[Hash32, Bytes] = field(
46
        default_factory=dict, compare=False
47
    )

get_code

Get the bytecode for a given code hash.

Return b"" for EMPTY_CODE_HASH.

def get_code(self, ​​code_hash: Hash32) -> Bytes:
50
        <snip>
55
        if code_hash == EMPTY_CODE_HASH:
56
            return b""
57
        return self._code_store[code_hash]

get_account_optional

Get the account at an address.

Return None if there is no account at the address.

def get_account_optional(self, ​​address: Address) -> Optional[Account]:
60
        <snip>
65
        return trie_get(self._main_trie, address)

get_storage

Get a storage value.

Return U256(0) if the key has not been set.

def get_storage(self, ​​address: Address, ​​key: Bytes32) -> U256:
68
        <snip>
73
        trie = self._storage_tries.get(address)
74
        if trie is None:
75
            return U256(0)
76
77
        value = trie_get(trie, key)
78
79
        assert isinstance(value, U256)
80
        return value

compute_state_root

Compute the state root after applying block_diff to the pre-state. The pre-state itself is not modified.

The diff's code_changes play no part: the Merkle Patricia Trie commits to accounts' code hashes, never to code contents, so account diffs alone determine the root.

Return the new state root.

def compute_state_root(self, ​​block_diff: BlockDiff) -> Root:
83
        <snip>
93
        main_trie = copy_trie(self._main_trie)
94
        storage_tries = {
95
            k: copy_trie(v)
96
            for k, v in self._storage_tries.items()
97
            if k not in block_diff.storage_clears
98
        }
99
100
        for address, account in block_diff.account_changes.items():
101
            trie_set(main_trie, address, account)
102
103
        for address, slots in block_diff.storage_changes.items():
104
            trie = storage_tries.get(address)
105
            if trie is None:
106
                trie = Trie(secured=True, default=U256(0))
107
                storage_tries[address] = trie
108
            for key, value in slots.items():
109
                trie_set(trie, key, value)
110
            if trie._data == {}:
111
                del storage_tries[address]
112
113
        def get_storage_root(addr: Address) -> Root:
114
            if addr in storage_tries:
115
                return root(storage_tries[addr])
116
            return EMPTY_TRIE_ROOT
117
118
        state_root_value = root(main_trie, get_storage_root=get_storage_root)
119
120
        return state_root_value

close_state

Free resources held by the state. Used by optimized implementations to release file descriptors.

def close_state(state: State) -> None:
124
    <snip>
128
    del state._main_trie
129
    del state._storage_tries
130
    del state._code_store

apply_changes_to_state

Apply block-level diff to the State for the next block.

Parameters

state : The state to update. diff : Account, storage, and code changes to apply.

def apply_changes_to_state(state: State, ​​diff: BlockDiff) -> None:
134
    <snip>
145
    for address in diff.storage_clears:
146
        state._storage_tries.pop(address, None)
147
148
    for address, account in diff.account_changes.items():
149
        trie_set(state._main_trie, address, account)
150
151
    for address, slots in diff.storage_changes.items():
152
        trie = state._storage_tries.get(address)
153
        if trie is None:
154
            trie = Trie(secured=True, default=U256(0))
155
            state._storage_tries[address] = trie
156
        for key, value in slots.items():
157
            trie_set(trie, key, value)
158
        if trie._data == {}:
159
            del state._storage_tries[address]
160
161
    state._code_store.update(diff.code_changes)

store_code

Store bytecode in State.

def store_code(state: State, ​​code: Bytes) -> Hash32:
165
    <snip>
168
    code_hash = keccak256(code)
169
    if code_hash != EMPTY_CODE_HASH:
170
        state._code_store[code_hash] = code
171
    return code_hash

set_account

Set an account in a State.

Setting to None deletes the account.

def set_account(state: State, ​​address: Address, ​​account: Optional[Account]) -> None:
179
    <snip>
184
    trie_set(state._main_trie, address, account)

set_storage

Set a storage value in a State.

Setting to U256(0) deletes the key.

def set_storage(state: State, ​​address: Address, ​​key: Bytes32, ​​value: U256) -> None:
193
    <snip>
198
    assert trie_get(state._main_trie, address) is not None
199
200
    trie = state._storage_tries.get(address)
201
    if trie is None:
202
        trie = Trie(secured=True, default=U256(0))
203
        state._storage_tries[address] = trie
204
    trie_set(trie, key, value)
205
    if trie._data == {}:
206
        del state._storage_tries[address]

state_root

Compute the state root of the current state.

def state_root(state: State) -> Root:
210
    <snip>
213
    return state.compute_state_root(BlockDiff())