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

account_has_storage

Check whether an account has any storage.

Only needed for EIP-7610.

def account_has_storage(self, ​​address: Address) -> bool:
83
        <snip>
88
        return address in self._storage_tries

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:
91
        <snip>
101
        main_trie = copy_trie(self._main_trie)
102
        storage_tries = {
103
            k: copy_trie(v)
104
            for k, v in self._storage_tries.items()
105
            if k not in block_diff.storage_clears
106
        }
107
108
        for address, account in block_diff.account_changes.items():
109
            trie_set(main_trie, address, account)
110
111
        for address, slots in block_diff.storage_changes.items():
112
            trie = storage_tries.get(address)
113
            if trie is None:
114
                trie = Trie(secured=True, default=U256(0))
115
                storage_tries[address] = trie
116
            for key, value in slots.items():
117
                trie_set(trie, key, value)
118
            if trie._data == {}:
119
                del storage_tries[address]
120
121
        def get_storage_root(addr: Address) -> Root:
122
            if addr in storage_tries:
123
                return root(storage_tries[addr])
124
            return EMPTY_TRIE_ROOT
125
126
        state_root_value = root(main_trie, get_storage_root=get_storage_root)
127
128
        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:
132
    <snip>
136
    del state._main_trie
137
    del state._storage_tries
138
    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:
142
    <snip>
153
    for address in diff.storage_clears:
154
        state._storage_tries.pop(address, None)
155
156
    for address, account in diff.account_changes.items():
157
        trie_set(state._main_trie, address, account)
158
159
    for address, slots in diff.storage_changes.items():
160
        trie = state._storage_tries.get(address)
161
        if trie is None:
162
            trie = Trie(secured=True, default=U256(0))
163
            state._storage_tries[address] = trie
164
        for key, value in slots.items():
165
            trie_set(trie, key, value)
166
        if trie._data == {}:
167
            del state._storage_tries[address]
168
169
    state._code_store.update(diff.code_changes)

store_code

Store bytecode in State.

def store_code(state: State, ​​code: Bytes) -> Hash32:
173
    <snip>
176
    code_hash = keccak256(code)
177
    if code_hash != EMPTY_CODE_HASH:
178
        state._code_store[code_hash] = code
179
    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:
187
    <snip>
192
    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:
201
    <snip>
206
    assert trie_get(state._main_trie, address) is not None
207
208
    trie = state._storage_tries.get(address)
209
    if trie is None:
210
        trie = Trie(secured=True, default=U256(0))
211
        state._storage_tries[address] = trie
212
    trie_set(trie, key, value)
213
    if trie._data == {}:
214
        del state._storage_tries[address]

state_root

Compute the state root of the current state.

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