ethereum.forks.bpo5.vm.interpreterethereum.forks.amsterdam.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¶
| 75 | STACK_DEPTH_LIMIT = Uint(1024) |
|---|
MAX_CODE_SIZE¶
| 64 | MAX_CODE_SIZE = 0x6000 |
|---|---|
| 76 | MAX_CODE_SIZE = 0x10000 |
MAX_INIT_CODE_SIZE¶
| 77 | 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.
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.
7. `regular_gas_used`: Regular gas used during execution.
8. `state_gas_used`: State gas used during execution.
9. `state_refund`: State gas refunded by `set_delegation` for
authorities that already existed in state. Subtracted from
`tx_state_gas` in block accounting so `block.gas_used`
matches the receipt `cumulative_gas_used`.
10. `created_target_alive`: Whether a top-level creation
transaction targeted an already-existent account.
| 80 | @final |
|---|
| 81 | @dataclass |
|---|
class MessageCallOutput:
gas_left¶
| 104 | gas_left: Uint |
|---|
refund_counter¶
| 105 | refund_counter: U256 |
|---|
logs¶
| 106 | logs: Tuple[Log, ...] |
|---|
accounts_to_delete¶
| 107 | accounts_to_delete: Set[Address] |
|---|
error¶
| 108 | error: Optional[EthereumException] |
|---|
return_data¶
| 109 | return_data: Bytes |
|---|
state_gas_left¶
| 110 | state_gas_left: Uint |
|---|
regular_gas_used¶
| 111 | regular_gas_used: Uint |
|---|
state_gas_used¶
| 112 | state_gas_used: int |
|---|
state_refund¶
| 113 | state_refund: Uint |
|---|
created_target_alive¶
| 114 | created_target_alive: bool |
|---|
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:
| 118 | <snip> |
|---|---|
| 133 | tx_state = message.tx_env.state |
| 134 | refund_counter = U256(0) |
| 135 | state_refund = Uint(0) |
| 136 | target_alive = False |
| 137 | if message.target == Bytes0(b""): |
| 138 | if account_deployable(tx_state, message.current_target): |
| 112 | evm = process_create_message(message) |
| 139 | target_alive = is_account_alive(tx_state, message.current_target) |
| 140 | evm = process_create_message(message) |
| 141 | else: |
| 142 | return MessageCallOutput( |
| 143 | gas_left=Uint(0), |
| 144 | refund_counter=U256(0), |
| 145 | logs=tuple(), |
| 146 | accounts_to_delete=set(), |
| 147 | error=AddressCollision(), |
| 148 | return_data=Bytes(b""), |
| 149 | state_gas_left=message.state_gas_reservoir, |
| 150 | regular_gas_used=message.gas, |
| 151 | state_gas_used=0, |
| 152 | state_refund=Uint(0), |
| 153 | created_target_alive=False, |
| 154 | ) |
| 155 | else: |
| 156 | if message.tx_env.authorizations != (): |
| 124 | refund_counter += set_delegation(message) |
| 157 | auth_state_refund, auth_regular_refund = set_delegation(message) |
| 158 | state_refund += auth_state_refund |
| 159 | refund_counter += U256(auth_regular_refund) |
| 160 | |
| 161 | delegated_address = get_delegated_code_address(message.code) |
| 162 | if delegated_address is not None: |
| 163 | message.disable_precompiles = True |
| 129 | message.accessed_addresses.add(delegated_address) |
| 164 | message.code = get_code( |
| 165 | tx_state, |
| 166 | get_account(tx_state, delegated_address).code_hash, |
| 167 | ) |
| 168 | message.code_address = delegated_address |
| 169 | |
| 170 | evm = process_message(message) |
| 171 | |
| 172 | if evm.error: |
| 173 | logs: Tuple[Log, ...] = () |
| 174 | accounts_to_delete = set() |
| 175 | else: |
| 176 | logs = evm.logs |
| 177 | accounts_to_delete = evm.accounts_to_delete |
| 178 | refund_counter += U256(evm.refund_counter) |
| 179 | |
| 180 | tx_end = TransactionEnd( |
| 181 | int(message.gas) - int(evm.gas_left), evm.output, evm.error |
| 182 | ) |
| 183 | evm_trace(evm, tx_end) |
| 184 | |
| 185 | return MessageCallOutput( |
| 186 | gas_left=evm.gas_left, |
| 187 | refund_counter=refund_counter, |
| 188 | logs=logs, |
| 189 | accounts_to_delete=accounts_to_delete, |
| 190 | error=evm.error, |
| 191 | return_data=evm.output, |
| 192 | state_gas_left=evm.state_gas_left, |
| 193 | regular_gas_used=evm.regular_gas_used, |
| 194 | state_gas_used=frame_state_gas_used(evm), |
| 195 | state_refund=state_refund, |
| 196 | created_target_alive=target_alive, |
| 197 | ) |
process_create_message ¶
Executes a call to create a smart contract.
Parameters
message : Transaction specific items.
Returns
evm: :py:class:
Items containing execution specific objects.~ethereum.forks.bpo5.vm.Evm~ethereum.forks.amsterdam.vm.Evm
def process_create_message(message: Message) -> Evm:
| 201 | <snip> |
|---|---|
| 215 | tx_state = message.tx_env.state |
| 216 | # take snapshot of state before processing the message |
| 217 | snapshot = copy_tx_state(tx_state) |
| 218 | |
| 219 | # If the address where the account is being created has storage, it is |
| 220 | # destroyed. This can only happen in the following highly unlikely |
| 221 | # circumstances: |
| 222 | # * The address created by a `CREATE` call collides with a subsequent |
| 223 | # `CREATE` or `CREATE2` call. |
| 224 | # * The first `CREATE` happened before Spurious Dragon and left empty |
| 225 | # code. |
| 226 | destroy_storage(tx_state, message.current_target) |
| 227 | |
| 228 | # In the previously mentioned edge case the preexisting storage is ignored |
| 229 | # for gas refund purposes. In order to do this we must track created |
| 230 | # accounts. This tracking is also needed to respect the constraints |
| 231 | # added to SELFDESTRUCT by EIP-6780. |
| 232 | mark_account_created(tx_state, message.current_target) |
| 233 | |
| 234 | increment_nonce(tx_state, message.current_target) |
| 235 | |
| 236 | evm = process_message(message) |
| 237 | if not evm.error: |
| 238 | contract_code = evm.output |
| 199 | contract_code_gas = ( |
| 200 | ulen(contract_code) * GasCosts.CODE_DEPOSIT_PER_BYTE |
| 201 | ) |
| 239 | try: |
| 240 | if len(contract_code) > 0: |
| 241 | if contract_code[0] == 0xEF: |
| 242 | raise InvalidContractPrefix |
| 206 | charge_gas(evm, contract_code_gas) |
| 243 | if len(contract_code) > MAX_CODE_SIZE: |
| 208 | raise OutOfGasError |
| 244 | raise OutOfGasError |
| 245 | # Hash cost for computing keccak256 of deployed bytecode |
| 246 | code_hash_gas = ( |
| 247 | GasCosts.OPCODE_KECCAK256_PER_WORD |
| 248 | * ceil32(ulen(contract_code)) |
| 249 | // Uint(32) |
| 250 | ) |
| 251 | charge_gas(evm, code_hash_gas) |
| 252 | code_deposit_state_gas = ( |
| 253 | ulen(contract_code) * StateGasCosts.COST_PER_STATE_BYTE |
| 254 | ) |
| 255 | charge_state_gas(evm, code_deposit_state_gas) |
| 256 | except ExceptionalHalt as error: |
| 257 | restore_tx_state(tx_state, snapshot) |
| 258 | refill_frame_state_gas(evm) |
| 259 | evm.regular_gas_used += evm.gas_left |
| 260 | evm.gas_left = Uint(0) |
| 261 | evm.output = b"" |
| 262 | evm.error = error |
| 263 | else: |
| 264 | set_code(tx_state, message.current_target, contract_code) |
| 265 | else: |
| 266 | restore_tx_state(tx_state, snapshot) |
| 267 | return evm |
process_message ¶
Move ether and execute the relevant code.
Parameters
message : Transaction specific items.
Returns
evm: :py:class:
Items containing execution specific objects~ethereum.forks.bpo5.vm.Evm~ethereum.forks.amsterdam.vm.Evm
def process_message(message: Message) -> Evm:
| 271 | <snip> |
|---|---|
| 285 | tx_state = message.tx_env.state |
| 286 | if message.depth > STACK_DEPTH_LIMIT: |
| 287 | raise StackDepthLimitError("Stack depth limit reached") |
| 288 | |
| 289 | code = message.code |
| 290 | valid_jump_destinations = get_valid_jump_destinations(code) |
| 291 | evm = Evm( |
| 292 | pc=Uint(0), |
| 293 | stack=[], |
| 294 | memory=bytearray(), |
| 295 | code=code, |
| 296 | gas_left=message.gas, |
| 297 | state_gas_left=message.state_gas_reservoir, |
| 298 | valid_jump_destinations=valid_jump_destinations, |
| 299 | logs=(), |
| 300 | refund_counter=0, |
| 301 | running=True, |
| 302 | message=message, |
| 303 | output=b"", |
| 304 | accounts_to_delete=set(), |
| 305 | return_data=b"", |
| 306 | error=None, |
| 307 | accessed_addresses=message.accessed_addresses, |
| 308 | accessed_storage_keys=message.accessed_storage_keys, |
| 309 | ) |
| 310 | |
| 261 | # take snapshot of state before processing the message |
| 262 | snapshot = copy_tx_state(tx_state) |
| 311 | snapshot = copy_tx_state(tx_state) |
| 312 | |
| 264 | if message.should_transfer_value and message.value != 0: |
| 265 | move_ether( |
| 266 | tx_state, |
| 267 | message.caller, |
| 268 | message.current_target, |
| 269 | message.value, |
| 270 | ) |
| 271 | |
| 272 | try: |
| 273 | if evm.message.code_address in PRE_COMPILED_CONTRACTS: |
| 313 | # Execute message code and handle errors |
| 314 | try: |
| 315 | if message.depth == Uint(0) and message.target != Bytes0(b""): |
| 316 | recipient = message.current_target |
| 317 | if message.value > U256(0) and not is_account_alive( |
| 318 | tx_state, recipient |
| 319 | ): |
| 320 | charge_state_gas(evm, StateGasCosts.NEW_ACCOUNT) |
| 321 | recipient_code = get_code( |
| 322 | tx_state, get_account(tx_state, recipient).code_hash |
| 323 | ) |
| 324 | delegated_address = get_delegated_code_address(recipient_code) |
| 325 | if delegated_address is not None: |
| 326 | charge_gas(evm, GasCosts.COLD_ACCOUNT_ACCESS) |
| 327 | evm.accessed_addresses.add(delegated_address) |
| 328 | |
| 329 | if message.should_transfer_value and message.value != 0: |
| 330 | move_ether( |
| 331 | tx_state, |
| 332 | message.caller, |
| 333 | message.current_target, |
| 334 | message.value, |
| 335 | ) |
| 336 | if message.caller != message.current_target: |
| 337 | emit_transfer_log( |
| 338 | evm, |
| 339 | message.caller, |
| 340 | message.current_target, |
| 341 | message.value, |
| 342 | ) |
| 343 | if evm.message.code_address in PRE_COMPILED_CONTRACTS: |
| 344 | if not message.disable_precompiles: |
| 345 | evm_trace(evm, PrecompileStart(evm.message.code_address)) |
| 346 | PRE_COMPILED_CONTRACTS[evm.message.code_address](evm) |
| 347 | evm_trace(evm, PrecompileEnd()) |
| 348 | else: |
| 349 | while evm.running and evm.pc < ulen(evm.code): |
| 350 | try: |
| 351 | op = Ops(evm.code[evm.pc]) |
| 352 | except ValueError as e: |
| 353 | raise InvalidOpcode(evm.code[evm.pc]) from e |
| 354 | |
| 355 | evm_trace(evm, OpStart(op)) |
| 356 | op_implementation[op](evm) |
| 357 | evm_trace(evm, OpEnd()) |
| 358 | |
| 359 | evm_trace(evm, EvmStop(Ops.STOP)) |
| 360 | |
| 361 | except ExceptionalHalt as error: |
| 362 | evm_trace(evm, OpException(error)) |
| 363 | refill_frame_state_gas(evm) |
| 364 | evm.regular_gas_used += evm.gas_left |
| 365 | evm.gas_left = Uint(0) |
| 366 | evm.output = b"" |
| 367 | evm.error = error |
| 368 | except Revert as error: |
| 369 | evm_trace(evm, OpException(error)) |
| 370 | refill_frame_state_gas(evm) |
| 371 | evm.error = error |
| 372 | |
| 373 | if evm.error: |
| 374 | restore_tx_state(tx_state, snapshot) |
| 375 | return evm |