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. |
|
Message-call into an account. |
Module Details
create
- create(evm)
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 = max_message_call_gas(Uint(evm.gas_left))
evm.gas_left -= create_message_gas
# 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,
should_transfer_value=True,
parent_evm=evm,
)
child_evm = process_create_message(child_message, evm.env)
if child_evm.error:
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)
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, gas, value, caller, to, code_address, should_transfer_value, memory_input_start_position, memory_input_size, memory_output_start_position, memory_output_size)
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,
should_transfer_value: bool,
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=gas,
value=value,
data=call_data,
code=code,
current_target=to,
depth=evm.message.depth + 1,
code_address=code_address,
should_transfer_value=should_transfer_value,
parent_evm=evm,
)
child_evm = process_message(child_message, evm.env)
if child_evm.error:
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)
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), ], ) _account_existscreate_gas_cost = account_exists(evm.env.state,( Uint(0) if value == 0 or is_account_alive(evm.env.state, to) else GAS_NEW_ACCOUNT ) transfer_gas_cost = Uint(0) if value == 0 else GAS_CALL_VALUE message_call_gas = calculate_message_call_gas( value, gas, Uint(evm.gas_left), extend_memory.cost, GAS_CALL + create_gas_cost + transfer_gas_cost, ) charge_gas(evm, message_call_gas.cost + extend_memory.cost) # OPERATION evm.memory += b"\x00" * extend_memory.expand_by sender_balance = Uint(0)get_account( evm.env.state, evm.message.current_target ).balance if _account_exists else GAS_NEW_ACCOUNT transfer_gas_cost = Uint(0) if value == 0 else GAS_CALL_VALUE message_call_gas = calculate_message_call_gas(sender_balance < value: push(evm.stack, U256(0)) evm.gas_left += message_call_gas.stipend else: generic_call( evm, message_call_gas.stipend, value, gas, Uint(evm.gas_left), extend_memory.cost, GAS_CALL + create_gas_cost + transfer_gas_cost,evm.message.current_target, to, to, True, memory_input_start_position, memory_input_size, memory_output_start_position, memory_output_size, ) charge_gas(evm, message_call_gas.cost + extend_memory.cost) # OPERATION evm.memoryPROGRAM COUNTER evm.pc += 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, True, memory_input_start_position, memory_input_size, memory_output_start_position, memory_output_size, ) # PROGRAM COUNTER evm.pc += 1
callcode
- callcode(evm)
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),
],
)
transfer_gas_cost = Uint(0) if value == 0 else GAS_CALL_VALUE
message_call_gas = calculate_message_call_gas(
value,
gas,
Uint(evm.gas_left),
extend_memory.cost,
GAS_CALL + transfer_gas_cost,
)
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,
True,
memory_input_start_position,
memory_input_size,
memory_output_start_position,
memory_output_size,
)
# PROGRAM COUNTER
evm.pc += 1
selfdestruct
- selfdestruct(evm)
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 gas_cost = GAS_SELF_DESTRUCT if ( not account_exists(evm.env.state,is_account_alive(evm.env.state, beneficiary) and get_account(evm.env.state, evm.message.current_target).balance != 0 ): gas_cost += GAS_SELF_DESTRUCT_NEW_ACCOUNT originator = evm.message.current_target refunded_accounts = evm.accounts_to_delete parent_evm = evm.message.parent_evm while parent_evm is not None: refunded_accounts.update(parent_evm.accounts_to_delete) parent_evm = parent_evm.message.parent_evm if originator not in refunded_accounts: evm.refund_counter += REFUND_SELF_DESTRUCT charge_gas(evm, gas_cost) # OPERATION 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) # mark beneficiary as touched if account_exists_and_is_empty(evm.env.state, beneficiary): gas_cost += GAS_SELF_DESTRUCT_NEW_ACCOUNT originator = evm.message.current_target refunded_accounts = evm.accounts_to_delete parent_evm = evm.message.parent_evm while parent_evm is not None: refunded_accounts.update(parent_evm.accounts_to_delete) parent_evm = parent_evm.message.parent_evm if originator not in refunded_accounts: evm.refund_counter += REFUND_SELF_DESTRUCT charge_gas(evm, gas_cost)evm.touched_accounts.add(beneficiary) # OPERATION 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
delegatecall
- delegatecall(evm)
Message-call into an account.
- Parameters
evm – The current EVM frame.
def delegatecall(evm: Evm) -> None:
# STACK
gas = Uint(pop(evm.stack))
code_address = to_address(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(
U256(0), gas, Uint(evm.gas_left), extend_memory.cost, GAS_CALL
)
charge_gas(evm, message_call_gas.cost + extend_memory.cost)
# OPERATION
evm.memory += b"\x00" * extend_memory.expand_by
generic_call(
evm,
message_call_gas.stipend,
evm.message.value,
evm.message.caller,
evm.message.current_target,
code_address,
False,
memory_input_start_position,
memory_input_size,
memory_output_start_position,
memory_output_size,
)
# PROGRAM COUNTER
evm.pc += 1