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 |