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

70
STACK_DEPTH_LIMIT = Uint(1024)

MAX_CODE_SIZE

64
MAX_CODE_SIZE = 0x6000
71
MAX_CODE_SIZE = 0x10000

MAX_INIT_CODE_SIZE

72
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.
75
@final
76
@dataclass
class MessageCallOutput:

gas_left

99
    gas_left: Uint

refund_counter

100
    refund_counter: U256

logs

101
    logs: Tuple[Log, ...]

accounts_to_delete

102
    accounts_to_delete: Set[Address]

error

103
    error: Optional[EthereumException]

return_data

104
    return_data: Bytes

state_gas_left

105
    state_gas_left: Uint

regular_gas_used

106
    regular_gas_used: Uint

state_gas_used

107
    state_gas_used: int

state_refund

108
    state_refund: Uint

created_target_alive

109
    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:
113
    <snip>
128
    tx_state = message.tx_env.state
129
    refund_counter = U256(0)
130
    state_refund = Uint(0)
131
    target_alive = False
132
    if message.target == Bytes0(b""):
133
        if account_deployable(tx_state, message.current_target):
112
            evm = process_create_message(message)
134
            target_alive = is_account_alive(tx_state, message.current_target)
135
            evm = process_create_message(message)
136
        else:
137
            return MessageCallOutput(
138
                gas_left=Uint(0),
139
                refund_counter=U256(0),
140
                logs=tuple(),
141
                accounts_to_delete=set(),
142
                error=AddressCollision(),
143
                return_data=Bytes(b""),
144
                state_gas_left=message.state_gas_reservoir,
145
                regular_gas_used=message.gas,
146
                state_gas_used=0,
147
                state_refund=Uint(0),
148
                created_target_alive=False,
149
            )
150
    else:
151
        if message.tx_env.authorizations != ():
124
            refund_counter += set_delegation(message)
152
            state_refund += set_delegation(message)
153
154
        delegated_address = get_delegated_code_address(message.code)
155
        if delegated_address is not None:
156
            message.disable_precompiles = True
157
            message.accessed_addresses.add(delegated_address)
158
            message.code = get_code(
159
                tx_state,
160
                get_account(tx_state, delegated_address).code_hash,
161
            )
162
            message.code_address = delegated_address
163
164
        evm = process_message(message)
165
166
    if evm.error:
167
        logs: Tuple[Log, ...] = ()
168
        accounts_to_delete = set()
169
    else:
170
        logs = evm.logs
171
        accounts_to_delete = evm.accounts_to_delete
172
        refund_counter += U256(evm.refund_counter)
173
174
    tx_end = TransactionEnd(
175
        int(message.gas) - int(evm.gas_left), evm.output, evm.error
176
    )
177
    evm_trace(evm, tx_end)
178
179
    return MessageCallOutput(
180
        gas_left=evm.gas_left,
181
        refund_counter=refund_counter,
182
        logs=logs,
183
        accounts_to_delete=accounts_to_delete,
184
        error=evm.error,
185
        return_data=evm.output,
186
        state_gas_left=evm.state_gas_left,
187
        regular_gas_used=evm.regular_gas_used,
188
        state_gas_used=evm.state_gas_used,
189
        state_refund=state_refund,
190
        created_target_alive=target_alive,
191
    )

process_create_message

Executes a call to create a smart contract.

Parameters

message : Transaction specific items.

Returns

evm: :py:class:~ethereum.forks.bpo5.vm.Evm~ethereum.forks.amsterdam.vm.Evm Items containing execution specific objects.

def process_create_message(message: Message) -> Evm:
195
    <snip>
209
    tx_state = message.tx_env.state
210
    # take snapshot of state before processing the message
211
    snapshot = copy_tx_state(tx_state)
212
213
    # If the address where the account is being created has storage, it is
214
    # destroyed. This can only happen in the following highly unlikely
215
    # circumstances:
216
    # * The address created by a `CREATE` call collides with a subsequent
217
    #   `CREATE` or `CREATE2` call.
218
    # * The first `CREATE` happened before Spurious Dragon and left empty
219
    #   code.
220
    destroy_storage(tx_state, message.current_target)
221
222
    # In the previously mentioned edge case the preexisting storage is ignored
223
    # for gas refund purposes. In order to do this we must track created
224
    # accounts. This tracking is also needed to respect the constraints
225
    # added to SELFDESTRUCT by EIP-6780.
226
    mark_account_created(tx_state, message.current_target)
227
228
    increment_nonce(tx_state, message.current_target)
229
230
    evm = process_message(message)
231
    if not evm.error:
232
        contract_code = evm.output
199
        contract_code_gas = (
200
            ulen(contract_code) * GasCosts.CODE_DEPOSIT_PER_BYTE
201
        )
233
        try:
234
            if len(contract_code) > 0:
235
                if contract_code[0] == 0xEF:
236
                    raise InvalidContractPrefix
206
            charge_gas(evm, contract_code_gas)
237
            if len(contract_code) > MAX_CODE_SIZE:
208
                raise OutOfGasError
238
                raise OutOfGasError
239
            # Hash cost for computing keccak256 of deployed bytecode
240
            code_hash_gas = (
241
                GasCosts.OPCODE_KECCAK256_PER_WORD
242
                * ceil32(ulen(contract_code))
243
                // Uint(32)
244
            )
245
            charge_gas(evm, code_hash_gas)
246
            code_deposit_state_gas = (
247
                ulen(contract_code) * StateGasCosts.COST_PER_STATE_BYTE
248
            )
249
            charge_state_gas(evm, code_deposit_state_gas)
250
        except ExceptionalHalt as error:
251
            restore_tx_state(tx_state, snapshot)
252
            refill_frame_state_gas(evm)
253
            evm.regular_gas_used += evm.gas_left
254
            evm.gas_left = Uint(0)
255
            evm.output = b""
256
            evm.error = error
257
        else:
258
            set_code(tx_state, message.current_target, contract_code)
259
    else:
260
        restore_tx_state(tx_state, snapshot)
261
    return evm

process_message

Move ether and execute the relevant code.

Parameters

message : Transaction specific items.

Returns

evm: :py:class:~ethereum.forks.bpo5.vm.Evm~ethereum.forks.amsterdam.vm.Evm Items containing execution specific objects

def process_message(message: Message) -> Evm:
265
    <snip>
279
    tx_state = message.tx_env.state
280
    if message.depth > STACK_DEPTH_LIMIT:
281
        raise StackDepthLimitError("Stack depth limit reached")
282
283
    code = message.code
284
    valid_jump_destinations = get_valid_jump_destinations(code)
285
    evm = Evm(
286
        pc=Uint(0),
287
        stack=[],
288
        memory=bytearray(),
289
        code=code,
290
        gas_left=message.gas,
291
        state_gas_left=message.state_gas_reservoir,
292
        valid_jump_destinations=valid_jump_destinations,
293
        logs=(),
294
        refund_counter=0,
295
        running=True,
296
        message=message,
297
        output=b"",
298
        accounts_to_delete=set(),
299
        return_data=b"",
300
        error=None,
301
        accessed_addresses=message.accessed_addresses,
302
        accessed_storage_keys=message.accessed_storage_keys,
303
    )
304
261
    # take snapshot of state before processing the message
262
    snapshot = copy_tx_state(tx_state)
305
    snapshot = copy_tx_state(tx_state)
306
307
    if message.should_transfer_value and message.value != 0:
308
        move_ether(
309
            tx_state,
310
            message.caller,
311
            message.current_target,
312
            message.value,
270
        )
313
        )
314
        if message.caller != message.current_target:
315
            emit_transfer_log(
316
                evm, message.caller, message.current_target, message.value
317
            )
318
272
    try:
319
    # Execute message code and handle errors
320
    try:
321
        if evm.message.code_address in PRE_COMPILED_CONTRACTS:
322
            if not message.disable_precompiles:
323
                evm_trace(evm, PrecompileStart(evm.message.code_address))
324
                PRE_COMPILED_CONTRACTS[evm.message.code_address](evm)
325
                evm_trace(evm, PrecompileEnd())
326
        else:
327
            while evm.running and evm.pc < ulen(evm.code):
328
                try:
329
                    op = Ops(evm.code[evm.pc])
330
                except ValueError as e:
331
                    raise InvalidOpcode(evm.code[evm.pc]) from e
332
333
                evm_trace(evm, OpStart(op))
334
                op_implementation[op](evm)
335
                evm_trace(evm, OpEnd())
336
337
            evm_trace(evm, EvmStop(Ops.STOP))
338
339
    except ExceptionalHalt as error:
340
        evm_trace(evm, OpException(error))
341
        refill_frame_state_gas(evm)
342
        evm.regular_gas_used += evm.gas_left
343
        evm.gas_left = Uint(0)
344
        evm.output = b""
345
        evm.error = error
346
    except Revert as error:
347
        evm_trace(evm, OpException(error))
348
        refill_frame_state_gas(evm)
349
        evm.error = error
350
351
    if evm.error:
352
        restore_tx_state(tx_state, snapshot)
353
    return evm