Ethereum Virtual Machine (EVM) Interpreter

Introduction

A straightforward interpreter that executes EVM code.

Module Contents

Classes

MessageCallOutput

Output of a particular message call

Functions

process_message_call

If message.current is empty then it creates a smart contract

process_create_message

Executes a call to create a smart contract.

process_message

Executes a call to create a smart contract.

execute_code

Executes bytecode present in the message.

Attributes

STACK_DEPTH_LIMIT

Module Details

STACK_DEPTH_LIMIT

STACK_DEPTH_LIMIT
STACK_DEPTH_LIMIT = U256(1024)

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. has_erred: True if execution has caused an error.

class MessageCallOutput
gas_left :ethereum.base_types.Uint
refund_counter :ethereum.base_types.U256
logs :Tuple[ethereum.homestead.fork_types.Log, Ellipsis]:Union[Tuple[], Tuple[ethereum.dao_fork.fork_types.Log, Ellipsis]]
accounts_to_delete :Set[ethereum.homestead.fork_types.Address]:Set[ethereum.dao_fork.fork_types.Address]
has_erred :bool

process_message_call

process_message_call(message, env)

If message.current 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.

  • env – External items required for EVM execution.

Returns

output – Output of the message call

Return type

MessageCallOutput

def process_message_call(
    message: Message, env: Environment
) -> MessageCallOutput:
    if message.target == Bytes0(b""):
        is_collision = account_has_code_or_nonce(
            env.state, message.current_target
        )
        if is_collision:
            return MessageCallOutput(Uint(0), U256(0), tuple(), set(), True)
        else:
            evm = process_create_message(message, env)
    else:
        evm = process_message(message, env)

    if evm.has_erred:
        logs: Tuple[Log, ...] = ()
        accounts_to_delete = set()
        refund_counter = U256(0)
    else:
        logs = evm.logs
        accounts_to_delete = evm.accounts_to_delete
        refund_counter = evm.refund_counter

    tx_end = TransactionEnd(
        message.gas - evm.gas_left, evm.output, evm.has_erred
    )
    evm_trace(evm, tx_end)

    return MessageCallOutput(
        gas_left=evm.gas_left,
        refund_counter=refund_counter,
        logs=logs,
        accounts_to_delete=accounts_to_delete,
        has_erred=evm.has_erred,
    )

process_create_message

process_create_message(message, env)

Executes a call to create a smart contract.

Parameters
  • message – Transaction specific items.

  • env – External items required for EVM execution.

Returns

evm – Items containing execution specific objects.

Return type

Evm

def process_create_message(message: Message, env: Environment) -> Evm:
    # take snapshot of state before processing the message
    begin_transaction(env.state)

    # If the address where the account is being created has storage, it is
    # destroyed. This can only happen in the following highly unlikely
    # circumstances:
    #evm = process_message(message, env)
    if not evm.has_erred:
        contract_code = evm.output
        contract_code_gas = len(contract_code) * The address created by two `CREATE` calls collide.
    # * The first `CREATE` left empty code.
    destroy_storage(env.state, message.current_target)GAS_CODE_DEPOSIT
        try:
            charge_gas(evm, contract_code_gas)
        except ExceptionalHalt:
            rollback_transaction(env.state)
            evm.gas_left = Uint(0)
            evm.has_erred = True
        else:
            set_code(env.state, message.current_target, contract_code)
            commit_transaction(env.state)
    else:
        rollback_transaction(env.state)
    return

    evm = process_message(message, env)
    if not evm.has_erred:
        contract_code = evm.output
        contract_code_gas = len(contract_code) * GAS_CODE_DEPOSIT
        try:
            charge_gas(evm, contract_code_gas)
        except ExceptionalHalt:
            rollback_transaction(env.state)
            evm.gas_left = Uint(0)
            evm.has_erred = True
        else:
            set_code(env.state, message.current_target, contract_code)
            commit_transaction(env.state)
    else:
        rollback_transaction(env.state)
    return evm

process_message

process_message(message, env)

Executes a call to create a smart contract.

Parameters
  • message – Transaction specific items.

  • env – External items required for EVM execution.

Returns

evm – Items containing execution specific objects

Return type

Evm

def process_message(message: Message, env: Environment) -> Evm:
    if message.depth > STACK_DEPTH_LIMIT:
        raise StackDepthLimitError("Stack depth limit reached")

    # take snapshot of state before processing the message
    begin_transaction(env.state)

    touch_account(env.state, message.current_target)

    if message.should_transfer_value and message.value != 0:
        move_ether(
            env.state, message.caller, message.current_target, message.value
        )

    evm = execute_code(message, env)
    if evm.has_erred:
        # revert state to the last saved checkpoint
        # since the message call resulted in an error
        rollback_transaction(env.state)
    else:
        commit_transaction(env.state)
    return evm

execute_code

execute_code(message, env)

Executes bytecode present in the message.

Parameters
  • message – Transaction specific items.

  • env – External items required for EVM execution.

Returns

evm – Items containing execution specific objects

Return type

ethereum.vm.EVM

def execute_code(message: Message, env: Environment) -> Evm:
    code = message.code
    valid_jump_destinations = get_valid_jump_destinations(code)

    evm = Evm(
        pc=Uint(0),
        stack=[],
        memory=bytearray(),
        code=code,
        gas_left=message.gas,
        env=env,
        valid_jump_destinations=valid_jump_destinations,
        logs=(),
        refund_counter=U256(0),
        running=True,
        message=message,
        output=b"",
        accounts_to_delete=set(),
        has_erred=False,
    )
    try:

        if evm.message.code_address in PRE_COMPILED_CONTRACTS:
            evm_trace(evm, PrecompileStart(evm.message.code_address))
            PRE_COMPILED_CONTRACTS[evm.message.code_address](evm)
            evm_trace(evm, PrecompileEnd())
            return evm

        while evm.running and evm.pc < len(evm.code):
            try:
                op = Ops(evm.code[evm.pc])
            except ValueError:
                raise InvalidOpcode(evm.code[evm.pc])

            evm_trace(evm, OpStart(op))
            op_implementation[op](evm)
            evm_trace(evm, OpEnd())

        evm_trace(evm, EvmStop(Ops.STOP))

    except ExceptionalHalt:
        evm_trace(evm, OpException())
        evm.gas_left = Uint(0)
        evm.has_erred = True
    return evm