ethereum.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

84
STACK_DEPTH_LIMIT = Uint(1024)

MAX_CODE_SIZE

85
MAX_CODE_SIZE = 0x10000

MAX_INIT_CODE_SIZE

86
MAX_INIT_CODE_SIZE = 2 * MAX_CODE_SIZE

TransactionOutput

Settled output of a transaction's top-level call.

Carry the figures fee settlement and the receipt need, so the frame itself never leaves the interpreter.

89
@final
90
@dataclass
class TransactionOutput:

gas_left

Execution gas remaining after execution.

99
    gas_left: ExecutionGas

refund_counter

Gas eligible for refund at the end of the transaction.

102
    refund_counter: U256

logs

Logs emitted during execution; empty when it failed.

105
    logs: Tuple[Log, ...]

accounts_to_delete

Accounts self-destructed during execution; empty when it failed.

108
    accounts_to_delete: Set[Address]

error

The error the execution halted with, if any.

111
    error: Optional[EthereumException]

return_data

The output of the execution.

114
    return_data: Bytes

state_gas_left

State gas remaining in the reservoir after execution.

117
    state_gas_left: StateGas

state_gas_used

Net state gas consumed; negative when refunds exceed charges.

120
    state_gas_used: int

charge_value_transfer_to_non_alive_account

Charge the state gas for creating recipient when a value transfer revives an account that is not alive.

def charge_value_transfer_to_non_alive_account(state: TransactionState, ​​gas_meter: GasMeter, ​​recipient: Address, ​​value: U256) -> None:
130
    <snip>
134
    if value > U256(0) and not is_account_alive(state, recipient):
135
        charge_state_gas_from_meter(gas_meter, StateGasCosts.NEW_ACCOUNT)

create_evm

Build the transaction's top-level frame.

Apply the EIP-7702 authorizations, charge the state-dependent dispatch costs to gas_meter, and resolve the code the frame runs. A preparation failure -- a creation-address collision or insufficient gas -- raises instead of building a frame, leaving the caller to roll back the state and gas the preparation charged and settle the transaction without dispatching.

def create_evm(block_env: BlockEnvironment, ​​tx_env: TransactionEnvironment, ​​gas_meter: GasMeter) -> Evm:
143
    <snip>
153
    current_target = tx_env.recipient
154
    if tx_env.is_create:
155
        call_data = Bytes(b"")
156
    else:
157
        call_data = tx_env.data
158
159
    code_address: Optional[Address] = None
160
    disable_precompiles = False
161
    accessed_addresses: Set[Address] = set()
162
    accessed_storage_keys = set(tx_env.access_list_storage_keys)
163
164
    ## Apply the 7702 delegations
165
    if tx_env.authorizations != ():
166
        accessed_authorities = set_delegation(block_env, tx_env, gas_meter)
167
        accessed_addresses.update(accessed_authorities)
168
        commit_state_gas(gas_meter)
169
170
    ## Warm up the access sets
171
    accessed_addresses.add(block_env.coinbase)
172
    accessed_addresses.update(PRE_COMPILED_CONTRACTS.keys())
173
    accessed_addresses.add(tx_env.origin)
174
    accessed_addresses.update(tx_env.access_list_addresses)
175
    accessed_addresses.add(current_target)
176
177
    ## Resolve dispatch and charge its state-dependent costs
178
    if tx_env.is_create:
179
        if not account_deployable(tx_env.state, current_target):
180
            raise AddressCollision()
181
182
        if (
183
            get_pre_state_account(tx_env.state, current_target)
184
            == EMPTY_ACCOUNT
185
        ):
186
            charge_state_gas_from_meter(gas_meter, StateGasCosts.NEW_ACCOUNT)
187
188
        code = tx_env.data
189
    else:
190
        charge_value_transfer_to_non_alive_account(
191
            tx_env.state, gas_meter, current_target, tx_env.value
192
        )
193
194
        code_address, disable_precompiles = resolve_delegated_code_address(
195
            tx_env.state, gas_meter, accessed_addresses, tx_env.recipient
196
        )
197
198
        code = get_code(
199
            tx_env.state,
200
            get_account(tx_env.state, code_address).code_hash,
201
        )
202
203
    ## Build the frame
204
    return Evm(
205
        # Context
206
        block_env=block_env,
207
        tx_env=tx_env,
208
        parent_evm=None,
209
        depth=Uint(0),
210
        # Call Parameters
211
        caller=tx_env.origin,
212
        current_target=current_target,
213
        value=tx_env.value,
214
        call_data=call_data,
215
        should_transfer_value=True,
216
        is_static=False,
217
        disable_precompiles=disable_precompiles,
218
        # Code
219
        code_address=code_address,
220
        code=code,
221
        valid_jump_destinations=get_valid_jump_destinations(code),
222
        # Machine State
223
        gas_meter=gas_meter,
224
        pc=Uint(0),
225
        stack=[],
226
        memory=bytearray(),
227
        return_data=b"",
228
        # Accrued Effects
229
        logs=(),
230
        accounts_to_delete=set(),
231
        accessed_addresses=accessed_addresses,
232
        accessed_storage_keys=accessed_storage_keys,
233
        # Outcome
234
        running=True,
235
        output=b"",
236
        error=None,
237
    )

process_top_level

Execute the top level of a transaction.

Prepare the transaction's top-level EVM frame and dispatch it: a contract creation or a call, per the transaction environment. A preparation failure rolls back everything the preparation changed and never dispatches; the transaction then settles as if execution halted at entry, forfeiting its entire gas grant.

Parameters

block_env : Environment for the Ethereum Virtual Machine. tx_env : Environment for the transaction.

Returns

tx_output : TransactionOutput The settled output of the top-level execution.

def process_top_level(block_env: BlockEnvironment, ​​tx_env: TransactionEnvironment) -> TransactionOutput:
244
    <snip>
266
    gas_meter = GasMeter(
267
        gas_left=tx_env.execution_gas_grant,
268
        state_gas_left=tx_env.state_gas_reservoir,
269
        state_gas_baseline=tx_env.state_gas_reservoir,
270
    )
271
272
    prep_snapshot = copy_tx_state(tx_env.state)
273
    try:
274
        evm = create_evm(block_env, tx_env, gas_meter)
275
    except ExceptionalHalt as halt:
276
        # The rollback also reverts any applied delegations, so their
277
        # state gas commit is undone with it: roll state gas back to
278
        # frame entry, refilling every state charge.
279
        restore_tx_state(tx_env.state, prep_snapshot)
280
        restore_state_gas_to_entry(gas_meter, tx_env.state_gas_reservoir)
281
        forfeit_remaining_gas(gas_meter)
282
        return TransactionOutput(
283
            gas_left=gas_meter.gas_left,
284
            refund_counter=U256(gas_meter.refund_counter),
285
            logs=(),
286
            accounts_to_delete=set(),
287
            error=halt,
288
            return_data=Bytes(b""),
289
            state_gas_left=gas_meter.state_gas_left,
290
            state_gas_used=tx_state_gas_used(
291
                gas_meter, tx_env.state_gas_reservoir
292
            ),
293
        )
294
295
    if tx_env.is_create:
296
        process_create(evm)
297
    else:
298
        process_call(evm)
299
300
    # A failed execution contributes no logs or self-destructs.
301
    if evm.error:
302
        logs: Tuple[Log, ...] = ()
303
        accounts_to_delete: Set[Address] = set()
304
    else:
305
        logs = evm.logs
306
        accounts_to_delete = evm.accounts_to_delete
307
308
    tx_end = TransactionEnd(
309
        int(tx_env.execution_gas_grant) - int(gas_meter.gas_left),
310
        evm.output,
311
        evm.error,
312
    )
313
    evm_trace(evm, tx_end)
314
315
    return TransactionOutput(
316
        gas_left=gas_meter.gas_left,
317
        refund_counter=U256(gas_meter.refund_counter),
318
        logs=logs,
319
        accounts_to_delete=accounts_to_delete,
320
        error=evm.error,
321
        return_data=evm.output,
322
        state_gas_left=gas_meter.state_gas_left,
323
        state_gas_used=tx_state_gas_used(
324
            gas_meter, tx_env.state_gas_reservoir
325
        ),
326
    )

process_create

Executes a call to create a smart contract.

Parameters

evm : Currently running evm.

Returns

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

def process_create(evm: Evm) -> Evm:
330
    <snip>
344
    tx_state = evm.tx_env.state
345
    # take snapshot of state before processing the message
346
    snapshot = copy_tx_state(tx_state)
347
348
    # If the address where the account is being created has storage, it is
349
    # destroyed. This can only happen in the following highly unlikely
350
    # circumstances:
351
    # * The address created by a `CREATE` call collides with a subsequent
352
    #   `CREATE` or `CREATE2` call.
353
    # * The first `CREATE` happened before Spurious Dragon and left empty
354
    #   code.
355
    destroy_storage(tx_state, evm.current_target)
356
357
    # In the previously mentioned edge case the preexisting storage is ignored
358
    # for gas refund purposes. In order to do this we must track created
359
    # accounts. This tracking is also needed to respect the constraints
360
    # added to SELFDESTRUCT by EIP-6780.
361
    mark_account_created(tx_state, evm.current_target)
362
363
    increment_nonce(tx_state, evm.current_target)
364
365
    evm = process_call(evm)
366
    if not evm.error:
367
        contract_code = evm.output
368
        try:
369
            if len(contract_code) > 0:
370
                if contract_code[0] == 0xEF:
371
                    raise InvalidContractPrefix
372
            if len(contract_code) > MAX_CODE_SIZE:
373
                raise OutOfGasError
374
            # Hash cost for computing keccak256 of deployed bytecode
375
            code_hash_gas = ExecutionGas(
376
                GasCosts.OPCODE_KECCAK256_PER_WORD
377
                * ceil32(ulen(contract_code))
378
                // Uint(32)
379
            )
380
            charge_gas(evm, code_hash_gas)
381
            code_deposit_state_gas = (
382
                ulen(contract_code) * StateGasCosts.COST_PER_STATE_BYTE
383
            )
384
            charge_state_gas(evm, code_deposit_state_gas)
385
        except ExceptionalHalt as error:
386
            restore_tx_state(tx_state, snapshot)
387
            # A create frame never applies authorizations, so its
388
            # baseline is still the frame's entry reservoir.
389
            restore_state_gas(evm.gas_meter)
390
            forfeit_remaining_gas(evm.gas_meter)
391
            evm.output = b""
392
            evm.error = error
393
        else:
394
            set_code(tx_state, evm.current_target, contract_code)
395
    else:
396
        restore_tx_state(tx_state, snapshot)
397
    return evm

process_call

Move ether and execute the relevant code.

Parameters

evm : The EVM frame to execute.

Returns

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

def process_call(evm: Evm) -> Evm:
401
    <snip>
415
    tx_state = evm.tx_env.state
416
    if evm.depth > STACK_DEPTH_LIMIT:
417
        raise StackDepthLimitError("Stack depth limit reached")
418
419
    snapshot = copy_tx_state(tx_state)
420
421
    # Execute message code and handle errors
422
    try:
423
        if evm.should_transfer_value and evm.value != 0:
424
            move_ether(
425
                tx_state,
426
                evm.caller,
427
                evm.current_target,
428
                evm.value,
429
            )
430
            if evm.caller != evm.current_target:
431
                emit_transfer_log(
432
                    evm,
433
                    evm.caller,
434
                    evm.current_target,
435
                    evm.value,
436
                )
437
        if evm.code_address in PRE_COMPILED_CONTRACTS:
438
            if not evm.disable_precompiles:
439
                evm_trace(evm, PrecompileStart(evm.code_address))
440
                PRE_COMPILED_CONTRACTS[evm.code_address](evm)
441
                evm_trace(evm, PrecompileEnd())
442
        else:
443
            while evm.running and evm.pc < ulen(evm.code):
444
                try:
445
                    op = Ops(evm.code[evm.pc])
446
                except ValueError as e:
447
                    raise InvalidOpcode(evm.code[evm.pc]) from e
448
449
                evm_trace(evm, OpStart(op))
450
                op_implementation[op](evm)
451
                evm_trace(evm, OpEnd())
452
453
            evm_trace(evm, EvmStop(Ops.STOP))
454
455
    except ExceptionalHalt as error:
456
        evm_trace(evm, OpException(error))
457
        # Frame settlement: refill state gas to the baseline, then
458
        # forfeit -- a halted frame returns no execution gas to its
459
        # parent. After these handlers the meter states exactly what
460
        # the frame gives back, so parents absorb unconditionally.
461
        restore_state_gas(evm.gas_meter)
462
        forfeit_remaining_gas(evm.gas_meter)
463
        evm.output = b""
464
        evm.error = error
465
    except Revert as error:
466
        evm_trace(evm, OpException(error))
467
        # Frame settlement: refill state gas to the baseline -- a
468
        # reverted frame returns its unspent `gas_left` to its parent.
469
        restore_state_gas(evm.gas_meter)
470
        evm.error = error
471
472
    if evm.error:
473
        restore_tx_state(tx_state, snapshot)
474
    return evm