Ethereum Virtual Machine (EVM) System Instructions
Table of Contents
Introduction
Implementations of the EVM system related instructions.
Module Contents
Functions
Creates a new account with associated code. |
|
Halts execution returning output data. |
|
Perform the core logic of the CALL* family of opcodes. |
|
Message-call into an account. |
|
Message-call into this account with alternative account’s code. |
|
Halt execution and register account for later deletion. |
Module Details
create
- create(evm: ethereum.frontier.vm.Evm) → None
Creates a new account with associated code.
- Parameters
evm – The current EVM frame.
def create(evm: Evm) -> None:
# This import causes a circular import error
# if it's not moved inside this method
from ...vm.interpreter import STACK_DEPTH_LIMIT, process_create_message
# STACK
endowment = pop(evm.stack)
memory_start_position = pop(evm.stack)
memory_size = pop(evm.stack)
# GAS
extend_memory = calculate_gas_extend_memory(
evm.memory, [(memory_start_position, memory_size)]
)
charge_gas(evm, GAS_CREATE + extend_memory.cost)
create_message_gas = evm.gas_left
evm.gas_left = U256(0)
# OPERATION
evm.memory += b"\x00" * extend_memory.expand_by
sender_address = evm.message.current_target
sender = get_account(evm.env.state, sender_address)
contract_address = compute_contract_address(
evm.message.current_target,
get_account(evm.env.state, evm.message.current_target).nonce,
)
if (
sender.balance < endowment
or sender.nonce == Uint(2**64 - 1)
or evm.message.depth + 1 > STACK_DEPTH_LIMIT
):
push(evm.stack, U256(0))
evm.gas_left += create_message_gas
elif account_has_code_or_nonce(evm.env.state, contract_address):
increment_nonce(evm.env.state, evm.message.current_target)
push(evm.stack, U256(0))
else:
call_data = memory_read_bytes(
evm.memory, memory_start_position, memory_size
)
increment_nonce(evm.env.state, evm.message.current_target)
child_message = Message(
caller=evm.message.current_target,
target=Bytes0(),
gas=create_message_gas,
value=endowment,
data=b"",
code=call_data,
current_target=contract_address,
depth=evm.message.depth + 1,
code_address=None,
)
child_evm = process_create_message(child_message, evm.env)
if child_evm.has_erred:
incorporate_child_on_error(evm, child_evm)
push(evm.stack, U256(0))
else:
incorporate_child_on_success(evm, child_evm)
push(
evm.stack, U256.from_be_bytes(child_evm.message.current_target)
)
# PROGRAM COUNTER
evm.pc += 1
return
- return_(evm: ethereum.frontier.vm.Evm) → None
Halts execution returning output data.
- Parameters
evm – The current EVM frame.
def return_(evm: Evm) -> None:
# STACK
memory_start_position = pop(evm.stack)
memory_size = pop(evm.stack)
# GAS
extend_memory = calculate_gas_extend_memory(
evm.memory, [(memory_start_position, memory_size)]
)
charge_gas(evm, GAS_ZERO + extend_memory.cost)
# OPERATION
evm.memory += b"\x00" * extend_memory.expand_by
evm.output = memory_read_bytes(
evm.memory, memory_start_position, memory_size
)
evm.running = False
# PROGRAM COUNTER
pass
generic_call
- generic_call(evm: ethereum.frontier.vm.Evm, gas: ethereum.base_types.Uint, value: ethereum.base_types.U256, caller: ethereum.frontier.fork_types.Address, to: ethereum.frontier.fork_types.Address, code_address: ethereum.frontier.fork_types.Address, memory_input_start_position: ethereum.base_types.U256, memory_input_size: ethereum.base_types.U256, memory_output_start_position: ethereum.base_types.U256, memory_output_size: ethereum.base_types.U256) → None
Perform the core logic of the CALL* family of opcodes.
def generic_call(
evm: Evm,
gas: Uint,
value: U256,
caller: Address,
to: Address,
code_address: Address,
memory_input_start_position: U256,
memory_input_size: U256,
memory_output_start_position: U256,
memory_output_size: U256,
) -> None:
from ...vm.interpreter import STACK_DEPTH_LIMIT, process_message
if evm.message.depth + 1 > STACK_DEPTH_LIMIT:
evm.gas_left += gas
push(evm.stack, U256(0))
return
call_data = memory_read_bytes(
evm.memory, memory_input_start_position, memory_input_size
)
code = get_account(evm.env.state, code_address).code
child_message = Message(
caller=caller,
target=to,
gas=U256(gas),
value=value,
data=call_data,
code=code,
current_target=to,
depth=evm.message.depth + 1,
code_address=code_address,
)
child_evm = process_message(child_message, evm.env)
if child_evm.has_erred:
incorporate_child_on_error(evm, child_evm)
push(evm.stack, U256(0))
else:
incorporate_child_on_success(evm, child_evm)
push(evm.stack, U256(1))
actual_output_size = min(memory_output_size, U256(len(child_evm.output)))
memory_write(
evm.memory,
memory_output_start_position,
child_evm.output[:actual_output_size],
)
call
- call(evm: ethereum.frontier.vm.Evm) → None
Message-call into an account.
- Parameters
evm – The current EVM frame.
def call(evm: Evm) -> None:
# STACK
gas = Uint(pop(evm.stack))
to = to_address(pop(evm.stack))
value = pop(evm.stack)
memory_input_start_position = pop(evm.stack)
memory_input_size = pop(evm.stack)
memory_output_start_position = pop(evm.stack)
memory_output_size = pop(evm.stack)
# GAS
extend_memory = calculate_gas_extend_memory(
evm.memory,
[
(memory_input_start_position, memory_input_size),
(memory_output_start_position, memory_output_size),
],
)
message_call_gas = calculate_message_call_gas(
evm.env.state, gas, to, value
)
charge_gas(evm, message_call_gas.cost + extend_memory.cost)
# OPERATION
evm.memory += b"\x00" * extend_memory.expand_by
sender_balance = get_account(
evm.env.state, evm.message.current_target
).balance
if sender_balance < value:
push(evm.stack, U256(0))
evm.gas_left += message_call_gas.stipend
else:
generic_call(
evm,
message_call_gas.stipend,
value,
evm.message.current_target,
to,
to,
memory_input_start_position,
memory_input_size,
memory_output_start_position,
memory_output_size,
)
# PROGRAM COUNTER
evm.pc += 1
callcode
- callcode(evm: ethereum.frontier.vm.Evm) → None
Message-call into this account with alternative account’s code.
- Parameters
evm – The current EVM frame.
def callcode(evm: Evm) -> None:
# STACK
gas = Uint(pop(evm.stack))
code_address = to_address(pop(evm.stack))
value = pop(evm.stack)
memory_input_start_position = pop(evm.stack)
memory_input_size = pop(evm.stack)
memory_output_start_position = pop(evm.stack)
memory_output_size = pop(evm.stack)
# GAS
to = evm.message.current_target
extend_memory = calculate_gas_extend_memory(
evm.memory,
[
(memory_input_start_position, memory_input_size),
(memory_output_start_position, memory_output_size),
],
)
message_call_gas = calculate_message_call_gas(
evm.env.state, gas, to, value
)
charge_gas(evm, message_call_gas.cost + extend_memory.cost)
# OPERATION
evm.memory += b"\x00" * extend_memory.expand_by
sender_balance = get_account(
evm.env.state, evm.message.current_target
).balance
if sender_balance < value:
push(evm.stack, U256(0))
evm.gas_left += message_call_gas.stipend
else:
generic_call(
evm,
message_call_gas.stipend,
value,
evm.message.current_target,
to,
code_address,
memory_input_start_position,
memory_input_size,
memory_output_start_position,
memory_output_size,
)
# PROGRAM COUNTER
evm.pc += 1
selfdestruct
- selfdestruct(evm: ethereum.frontier.vm.Evm) → None
Halt execution and register account for later deletion.
- Parameters
evm – The current EVM frame.
def selfdestruct(evm: Evm) -> None:
# STACK
beneficiary = to_address(pop(evm.stack))
# GAS
pass
# OPERATION
originator = evm.message.current_target
beneficiary_balance = get_account(evm.env.state, beneficiary).balance
originator_balance = get_account(evm.env.state, originator).balance
# First Transfer to beneficiary
set_account_balance(
evm.env.state, beneficiary, beneficiary_balance + originator_balance
)
# Next, Zero the balance of the address being deleted (must come after
# sending to beneficiary in case the contract named itself as the
# beneficiary).
set_account_balance(evm.env.state, originator, U256(0))
# register account for deletion
evm.accounts_to_delete.add(originator)
# HALT the execution
evm.running = False
# PROGRAM COUNTER
pass