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. |
|
Message-call into an account. |
|
Stop execution and revert state changes, without consuming all provided gas |
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 -= U256(create_message_gas) # OPERATION ensure(not evm.message.is_static, WriteInStaticContext) evm.memory += b"\x00" * extend_memory.expand_by evm.return_data = b"" 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=U256(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, is_static=False, ) child_evm = process_create_message(child_message, evm.env) if child_evm.has_erred: incorporate_child_on_error(evm, child_evm) evm.return_data = child_evm.output push(evm.stack, U256(0)) else: incorporate_child_on_success(evm, child_evm) evm.return_data = b"" 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, is_staticcall, 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, is_staticcall: 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 evm.return_data = b"" 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, should_transfer_value=should_transfer_value, is_static=True if is_staticcall else evm.message.is_static, ) child_evm = process_message(child_message, evm.env) if child_evm.has_erred: incorporate_child_on_error(evm, child_evm) evm.return_data = child_evm.output push(evm.stack, U256(0)) else: incorporate_child_on_success(evm, child_evm) evm.return_data = child_evm.output 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), ], ) create_gas_cost = ( 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 ensure(not evm.message.is_static or value == U256(0), WriteInStaticContext) 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.return_data = b"" evm.gas_left += message_call_gas.stipend else: generic_call( evm, message_call_gas.stipend, value, evm.message.current_target, to, to, True, False, 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.return_data = b"" evm.gas_left += message_call_gas.stipend else: generic_call( evm, message_call_gas.stipend, value, evm.message.current_target, to, code_address, True, False, 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
if (
not is_account_alive(evm.env.state, beneficiary)
and get_account(evm.env.state, evm.message.current_target).balance != 0
):
charge_gas(evm, GAS_SELF_DESTRUCT + GAS_SELF_DESTRUCT_NEW_ACCOUNT)
else:
charge_gas(evm, GAS_SELF_DESTRUCT)
# OPERATION
ensure(not evm.message.is_static, WriteInStaticContext)
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)
# mark beneficiary as touched
if account_exists_and_is_empty(evm.env.state, beneficiary):
evm.touched_accounts.add(beneficiary)
# 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,
False,
memory_input_start_position,
memory_input_size,
memory_output_start_position,
memory_output_size,
)
# PROGRAM COUNTER
evm.pc += 1
staticcall
- staticcall(evm)
Message-call into an account.
- Parameters
evm – The current EVM frame.
def staticcall(evm: Evm) -> None:
# STACK
gas = Uint(pop(evm.stack))
to = 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,
U256(0),
evm.message.current_target,
to,
to,
True,
True,
memory_input_start_position,
memory_input_size,
memory_output_start_position,
memory_output_size,
)
# PROGRAM COUNTER
evm.pc += 1
revert
- revert(evm)
Stop execution and revert state changes, without consuming all provided gas and also has the ability to return a reason :param evm: The current EVM frame.
def revert(evm: Evm) -> None:
# STACK
memory_start_index = pop(evm.stack)
size = pop(evm.stack)
# GAS
extend_memory = calculate_gas_extend_memory(
evm.memory, [(memory_start_index, size)]
)
charge_gas(evm, extend_memory.cost)
# OPERATION
evm.memory += b"\x00" * extend_memory.expand_by
output = memory_read_bytes(evm.memory, memory_start_index, size)
evm.output = bytes(output)
raise Revert
# PROGRAM COUNTER
pass