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

69
STACK_DEPTH_LIMIT = Uint(1024)

MAX_CODE_SIZE

70
MAX_CODE_SIZE = 0x10000

MAX_INIT_CODE_SIZE

71
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.
  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`.
74
@final
75
@dataclass
class MessageCallOutput:

gas_left

96
    gas_left: Uint

refund_counter

97
    refund_counter: U256

logs

98
    logs: Tuple[Log, ...]

accounts_to_delete

99
    accounts_to_delete: Set[Address]

error

100
    error: Optional[EthereumException]

return_data

101
    return_data: Bytes

state_gas_left

102
    state_gas_left: Uint

regular_gas_used

103
    regular_gas_used: Uint

state_gas_used

104
    state_gas_used: int

state_refund

105
    state_refund: Uint

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

process_create_message

Executes a call to create a smart contract.

Parameters

message : Transaction specific items.

Returns

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

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

process_message

Move ether and execute the relevant code.

Parameters

message : Transaction specific items.

Returns

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

def process_message(message: Message) -> Evm:
256
    <snip>
270
    tx_state = message.tx_env.state
271
    if message.depth > STACK_DEPTH_LIMIT:
272
        raise StackDepthLimitError("Stack depth limit reached")
273
274
    code = message.code
275
    valid_jump_destinations = get_valid_jump_destinations(code)
276
    evm = Evm(
277
        pc=Uint(0),
278
        stack=[],
279
        memory=bytearray(),
280
        code=code,
281
        gas_left=message.gas,
282
        state_gas_left=message.state_gas_reservoir,
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
296
    snapshot = copy_tx_state(tx_state)
297
298
    if message.should_transfer_value and message.value != 0:
299
        move_ether(
300
            tx_state,
301
            message.caller,
302
            message.current_target,
303
            message.value,
304
        )
305
        if message.caller != message.current_target:
306
            emit_transfer_log(
307
                evm, message.caller, message.current_target, message.value
308
            )
309
310
    # Execute message code and handle errors
311
    try:
312
        if evm.message.code_address in PRE_COMPILED_CONTRACTS:
313
            if not message.disable_precompiles:
314
                evm_trace(evm, PrecompileStart(evm.message.code_address))
315
                PRE_COMPILED_CONTRACTS[evm.message.code_address](evm)
316
                evm_trace(evm, PrecompileEnd())
317
        else:
318
            while evm.running and evm.pc < ulen(evm.code):
319
                try:
320
                    op = Ops(evm.code[evm.pc])
321
                except ValueError as e:
322
                    raise InvalidOpcode(evm.code[evm.pc]) from e
323
324
                evm_trace(evm, OpStart(op))
325
                op_implementation[op](evm)
326
                evm_trace(evm, OpEnd())
327
328
            evm_trace(evm, EvmStop(Ops.STOP))
329
330
    except ExceptionalHalt as error:
331
        evm_trace(evm, OpException(error))
332
        evm.regular_gas_used += evm.gas_left
333
        evm.gas_left = Uint(0)
334
        evm.output = b""
335
        evm.error = error
336
    except Revert as error:
337
        evm_trace(evm, OpException(error))
338
        evm.error = error
339
340
    if evm.error:
341
        restore_tx_state(tx_state, snapshot)
342
    return evm