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 = Uint(0) # OPERATION evm.memory += b"\x00" * extend_memory.expand_by sender_addresscreate_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 = evm.message.current_target sendermemory_read_bytes( evm.memory, memory_start_position, memory_size ) increment_nonce(evm.env.state, evm.message.current_target) child_message = get_account(evm.env.state, sender_address) contract_addressMessage( 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 = compute_contract_address( evm.message.current_target, get_account(evm.env.state, evm.message.current_target).nonce, )process_create_message(child_message, evm.env) if ( sender.balance < endowment or sender.nonce == Uint(2**64 - 1) or evm.message.depth +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 > 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_exists = account_exists(evm.env.state, to) create_gas_cost = Uint(0) 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( 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 = get_account( evm.env.state, gas,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, valueto, 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, gas,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, valuecode_address, 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, 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_ZEROGAS_SELF_DESTRUCT if not account_exists(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) # 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, GAS_CALLmessage_call_gas.cost + gas + extend_memory.cost) # OPERATION evm.memory += b"\x00" * extend_memory.expand_by generic_call( evm, gas,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