Ethereum Virtual Machine (EVM) Interpreter
Table of Contents
Introduction
A straightforward interpreter that executes EVM code.
Module Contents
Classes
Output of a particular message call |
Functions
If message.current is empty then it creates a smart contract |
|
Executes a call to create a smart contract. |
|
Executes a call to create a smart contract. |
|
Executes bytecode present in the message. |
|
Collects all the accounts that were marked for deletion by the |
|
Adds up the gas that was refunded in each execution frame during the |
Attributes
Module Details
STACK_DEPTH_LIMIT
- STACK_DEPTH_LIMIT
STACK_DEPTH_LIMIT = U256(1024)
MessageCallOutput
Output of a particular message call
Contains the following:
gas_left: remaining gas after execution.
refund_counter: gas to refund after execution.
logs: list of Log generated during execution.
accounts_to_delete: Contracts which have self-destructed.
has_erred: True if execution has caused an error.
process_message_call
- process_message_call(message: ethereum.dao_fork.vm.Message, env: ethereum.dao_fork.vm.Environment) → MessageCallOutput
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(U256(0), U256(0), tuple(), set(), True)
else:
evm = process_create_message(message, env)
else:
evm = process_message(message, env)
accounts_to_delete = collect_accounts_to_delete(evm)
refund_counter = (
calculate_gas_refund(evm)
+ len(accounts_to_delete) * REFUND_SELF_DESTRUCT
)
return MessageCallOutput(
gas_left=evm.gas_left,
refund_counter=refund_counter,
logs=evm.logs if not evm.has_erred else (),
accounts_to_delete=accounts_to_delete,
has_erred=evm.has_erred,
)
process_create_message
- process_create_message(message: ethereum.dao_fork.vm.Message, env: ethereum.dao_fork.vm.Environment) → ethereum.dao_fork.vm.Evm
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
def process_create_message(message: Message, env: Environment) -> Evm:
# take snapshot of state before processing the message
begin_transaction(env.state)
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 = U256(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: ethereum.dao_fork.vm.Message, env: ethereum.dao_fork.vm.Environment) → ethereum.dao_fork.vm.Evm
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
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: ethereum.dao_fork.vm.Message, env: ethereum.dao_fork.vm.Environment) → ethereum.dao_fork.vm.Evm
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,
children=[],
)
try:
if evm.message.code_address in PRE_COMPILED_CONTRACTS:
evm_trace(evm, evm.message.code_address)
PRE_COMPILED_CONTRACTS[evm.message.code_address](evm)
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, op)
op_implementation[op](evm)
except ExceptionalHalt:
evm.gas_left = U256(0)
evm.has_erred = True
return evm
collect_accounts_to_delete
- collect_accounts_to_delete(evm: ethereum.dao_fork.vm.Evm) → Set[ethereum.dao_fork.fork_types.Address]
Collects all the accounts that were marked for deletion by the SELFDESTRUCT opcode.
- Parameters
evm – The current EVM frame.
- Returns
accounts_to_delete – returns all the accounts need marked for deletion by the SELFDESTRUCT opcode.
- Return type
set
def collect_accounts_to_delete(evm: Evm) -> Set[Address]:
if evm.has_erred:
return set()
else:
return set(
chain(
evm.accounts_to_delete,
*(collect_accounts_to_delete(child) for child in evm.children),
)
)
calculate_gas_refund
- calculate_gas_refund(evm: ethereum.dao_fork.vm.Evm) → ethereum.base_types.U256
Adds up the gas that was refunded in each execution frame during the message call.
- Parameters
evm – The current EVM frame.
- Returns
gas_refund – returns the total gas that needs to be refunded after executing the message call.
- Return type
ethereum.base_types.U256
def calculate_gas_refund(evm: Evm) -> U256:
if evm.has_erred:
return U256(0)
else:
return evm.refund_counter + sum(
calculate_gas_refund(child_evm) for child_evm in evm.children
)