ethereum.forks.osaka.vm.interpreter
Ethereum Virtual Machine (EVM) Interpreter.
.. contents:: Table of Contents :backlinks: none :local:
Introduction
A straightforward interpreter that executes EVM code.
STACK_DEPTH_LIMIT
64 | STACK_DEPTH_LIMIT = Uint(1024) |
---|
MAX_CODE_SIZE
65 | MAX_CODE_SIZE = 0x6000 |
---|
MAX_INIT_CODE_SIZE
66 | MAX_INIT_CODE_SIZE = 2 * MAX_CODE_SIZE |
---|
MessageCallOutput
Output of a particular message call.
Contains the following:
1. `gas_left`: remaining gas after execution.
2. `refund_counter`: gas to refund after execution.
3. `logs`: list of `Log` generated during execution.
4. `accounts_to_delete`: Contracts which have self-destructed.
5. `error`: The error from the execution if any.
6. `return_data`: The output of the execution.
69 | @dataclass |
---|
class MessageCallOutput:
gas_left
84 | gas_left: Uint |
---|
refund_counter
85 | refund_counter: U256 |
---|
logs
86 | logs: Tuple[Log, ...] |
---|
accounts_to_delete
87 | accounts_to_delete: Set[Address] |
---|
error
88 | error: Optional[EthereumException] |
---|
return_data
89 | return_data: Bytes |
---|
process_message_call
If message.target
is empty then it creates a smart contract
else it executes a call from the message.caller
to the message.target
.
Parameters
message : Transaction specific items.
Returns
output : MessageCallOutput
Output of the message call
def process_message_call(message: Message) -> MessageCallOutput:
93 | """ |
---|---|
94 | If `message.target` is empty then it creates a smart contract |
95 | else it executes a call from the `message.caller` to the `message.target`. |
96 |
|
97 | Parameters |
98 | ---------- |
99 | message : |
100 | Transaction specific items. |
101 |
|
102 | Returns |
103 | ------- |
104 | output : `MessageCallOutput` |
105 | Output of the message call |
106 |
|
107 | """ |
108 | block_env = message.block_env |
109 | refund_counter = U256(0) |
110 | if message.target == Bytes0(b""): |
111 | is_collision = account_has_code_or_nonce( |
112 | block_env.state, message.current_target |
113 | ) or account_has_storage(block_env.state, message.current_target) |
114 | if is_collision: |
115 | return MessageCallOutput( |
116 | Uint(0), |
117 | U256(0), |
118 | tuple(), |
119 | set(), |
120 | AddressCollision(), |
121 | Bytes(b""), |
122 | ) |
123 | else: |
124 | evm = process_create_message(message) |
125 | else: |
126 | if message.tx_env.authorizations != (): |
127 | refund_counter += set_delegation(message) |
128 |
|
129 | delegated_address = get_delegated_code_address(message.code) |
130 | if delegated_address is not None: |
131 | message.disable_precompiles = True |
132 | message.accessed_addresses.add(delegated_address) |
133 | message.code = get_account(block_env.state, delegated_address).code |
134 | message.code_address = delegated_address |
135 |
|
136 | evm = process_message(message) |
137 | |
138 | if evm.error: |
139 | logs: Tuple[Log, ...] = () |
140 | accounts_to_delete = set() |
141 | else: |
142 | logs = evm.logs |
143 | accounts_to_delete = evm.accounts_to_delete |
144 | refund_counter += U256(evm.refund_counter) |
145 | |
146 | tx_end = TransactionEnd( |
147 | int(message.gas) - int(evm.gas_left), evm.output, evm.error |
148 | ) |
149 | evm_trace(evm, tx_end) |
150 | |
151 | return MessageCallOutput( |
152 | gas_left=evm.gas_left, |
153 | refund_counter=refund_counter, |
154 | logs=logs, |
155 | accounts_to_delete=accounts_to_delete, |
156 | error=evm.error, |
157 | return_data=evm.output, |
158 | ) |
process_create_message
Executes a call to create a smart contract.
Parameters
message : Transaction specific items.
Returns
evm: :py:class:~ethereum.forks.osaka.vm.Evm
Items containing execution specific objects.
def process_create_message(message: Message) -> Evm:
162 | """ |
---|---|
163 | Executes a call to create a smart contract. |
164 |
|
165 | Parameters |
166 | ---------- |
167 | message : |
168 | Transaction specific items. |
169 |
|
170 | Returns |
171 | ------- |
172 | evm: :py:class:`~ethereum.forks.osaka.vm.Evm` |
173 | Items containing execution specific objects. |
174 |
|
175 | """ |
176 | state = message.block_env.state |
177 | transient_storage = message.tx_env.transient_storage |
178 | # take snapshot of state before processing the message |
179 | begin_transaction(state, transient_storage) |
180 | |
181 | # If the address where the account is being created has storage, it is |
182 | # destroyed. This can only happen in the following highly unlikely |
183 | # circumstances: |
184 | # * The address created by a `CREATE` call collides with a subsequent |
185 | # `CREATE` or `CREATE2` call. |
186 | # * The first `CREATE` happened before Spurious Dragon and left empty |
187 | # code. |
188 | destroy_storage(state, message.current_target) |
189 | |
190 | # In the previously mentioned edge case the preexisting storage is ignored |
191 | # for gas refund purposes. In order to do this we must track created |
192 | # accounts. This tracking is also needed to respect the constraints |
193 | # added to SELFDESTRUCT by EIP-6780. |
194 | mark_account_created(state, message.current_target) |
195 | |
196 | increment_nonce(state, message.current_target) |
197 | evm = process_message(message) |
198 | if not evm.error: |
199 | contract_code = evm.output |
200 | contract_code_gas = Uint(len(contract_code)) * GAS_CODE_DEPOSIT |
201 | try: |
202 | if len(contract_code) > 0: |
203 | if contract_code[0] == 0xEF: |
204 | raise InvalidContractPrefix |
205 | charge_gas(evm, contract_code_gas) |
206 | if len(contract_code) > MAX_CODE_SIZE: |
207 | raise OutOfGasError |
208 | except ExceptionalHalt as error: |
209 | rollback_transaction(state, transient_storage) |
210 | evm.gas_left = Uint(0) |
211 | evm.output = b"" |
212 | evm.error = error |
213 | else: |
214 | set_code(state, message.current_target, contract_code) |
215 | commit_transaction(state, transient_storage) |
216 | else: |
217 | rollback_transaction(state, transient_storage) |
218 | return evm |
process_message
Move ether and execute the relevant code.
Parameters
message : Transaction specific items.
Returns
evm: :py:class:~ethereum.forks.osaka.vm.Evm
Items containing execution specific objects
def process_message(message: Message) -> Evm:
222 | """ |
---|---|
223 | Move ether and execute the relevant code. |
224 |
|
225 | Parameters |
226 | ---------- |
227 | message : |
228 | Transaction specific items. |
229 |
|
230 | Returns |
231 | ------- |
232 | evm: :py:class:`~ethereum.forks.osaka.vm.Evm` |
233 | Items containing execution specific objects |
234 |
|
235 | """ |
236 | state = message.block_env.state |
237 | transient_storage = message.tx_env.transient_storage |
238 | if message.depth > STACK_DEPTH_LIMIT: |
239 | raise StackDepthLimitError("Stack depth limit reached") |
240 | |
241 | # take snapshot of state before processing the message |
242 | begin_transaction(state, transient_storage) |
243 | |
244 | if message.should_transfer_value and message.value != 0: |
245 | move_ether( |
246 | state, message.caller, message.current_target, message.value |
247 | ) |
248 | |
249 | evm = execute_code(message) |
250 | if evm.error: |
251 | # revert state to the last saved checkpoint |
252 | # since the message call resulted in an error |
253 | rollback_transaction(state, transient_storage) |
254 | else: |
255 | commit_transaction(state, transient_storage) |
256 | return evm |
execute_code
Executes bytecode present in the message
.
Parameters
message : Transaction specific items.
Returns
evm: ethereum.vm.EVM
Items containing execution specific objects
def execute_code(message: Message) -> Evm:
260 | """ |
---|---|
261 | Executes bytecode present in the `message`. |
262 |
|
263 | Parameters |
264 | ---------- |
265 | message : |
266 | Transaction specific items. |
267 |
|
268 | Returns |
269 | ------- |
270 | evm: `ethereum.vm.EVM` |
271 | Items containing execution specific objects |
272 |
|
273 | """ |
274 | code = message.code |
275 | valid_jump_destinations = get_valid_jump_destinations(code) |
276 | |
277 | evm = Evm( |
278 | pc=Uint(0), |
279 | stack=[], |
280 | memory=bytearray(), |
281 | code=code, |
282 | gas_left=message.gas, |
283 | valid_jump_destinations=valid_jump_destinations, |
284 | logs=(), |
285 | refund_counter=0, |
286 | running=True, |
287 | message=message, |
288 | output=b"", |
289 | accounts_to_delete=set(), |
290 | return_data=b"", |
291 | error=None, |
292 | accessed_addresses=message.accessed_addresses, |
293 | accessed_storage_keys=message.accessed_storage_keys, |
294 | ) |
295 | try: |
296 | if evm.message.code_address in PRE_COMPILED_CONTRACTS: |
297 | if message.disable_precompiles: |
298 | return evm |
299 | evm_trace(evm, PrecompileStart(evm.message.code_address)) |
300 | PRE_COMPILED_CONTRACTS[evm.message.code_address](evm) |
301 | evm_trace(evm, PrecompileEnd()) |
302 | return evm |
303 |
|
304 | while evm.running and evm.pc < ulen(evm.code): |
305 | try: |
306 | op = Ops(evm.code[evm.pc]) |
307 | except ValueError as e: |
308 | raise InvalidOpcode(evm.code[evm.pc]) from e |
309 |
|
310 | evm_trace(evm, OpStart(op)) |
311 | op_implementation[op](evm) |
312 | evm_trace(evm, OpEnd()) |
313 |
|
314 | evm_trace(evm, EvmStop(Ops.STOP)) |
315 |
|
316 | except ExceptionalHalt as error: |
317 | evm_trace(evm, OpException(error)) |
318 | evm.gas_left = Uint(0) |
319 | evm.output = b"" |
320 | evm.error = error |
321 | except Revert as error: |
322 | evm_trace(evm, OpException(error)) |
323 | evm.error = error |
324 | return evm |