ethereum.forks.amsterdam.vm.interpreterethereum.forks.bogota.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

79
STACK_DEPTH_LIMIT = Uint(1024)

MAX_CODE_SIZE

80
MAX_CODE_SIZE = 0x10000

MAX_INIT_CODE_SIZE

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

84
@final
85
@dataclass
class TransactionOutput:

gas_left

Execution gas remaining after execution.

94
    gas_left: ExecutionGas

refund_counter

Gas eligible for refund at the end of the transaction.

97
    refund_counter: U256

logs

Logs emitted during execution; empty when it failed.

100
    logs: Tuple[Log, ...]

accounts_to_delete

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

103
    accounts_to_delete: Set[Address]

error

The error the execution halted with, if any.

106
    error: Optional[EthereumException]

return_data

The output of the execution.

109
    return_data: Bytes

state_gas_left

State gas remaining in the reservoir after execution.

112
    state_gas_left: StateGas

state_gas_used

Net state gas consumed; negative when refunds exceed charges.

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

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

process_create

Executes a call to create a smart contract.

Parameters

evm : Currently running evm.

Returns

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

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

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