Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions src/ethereum/forks/amsterdam/vm/instructions/log.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ def log_n(evm: Evm, num_topics: int) -> None:
The number of topics to be included in the log entry.

"""
if evm.message.is_static:
raise WriteInStaticContext

# STACK
memory_start_index = pop(evm.stack)
size = pop(evm.stack)
Expand All @@ -66,8 +69,6 @@ def log_n(evm: Evm, num_topics: int) -> None:

# OPERATION
evm.memory += b"\x00" * extend_memory.expand_by
if evm.message.is_static:
raise WriteInStaticContext
log_entry = Log(
address=evm.message.current_target,
topics=tuple(topics),
Expand Down
17 changes: 17 additions & 0 deletions src/ethereum/forks/arrow_glacier/vm/gas.py
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,23 @@ class MessageCallGas:
sub_call: Uint


def check_gas(evm: Evm, amount: Uint) -> None:
"""
Checks if `amount` gas is available without charging it.
Raises OutOfGasError if insufficient gas.

Parameters
----------
evm :
The current EVM.
amount :
The amount of gas to check.

"""
if evm.gas_left < amount:
raise OutOfGasError


def charge_gas(evm: Evm, amount: Uint) -> None:
"""
Subtracts `amount` from `evm.gas_left`.
Expand Down
5 changes: 3 additions & 2 deletions src/ethereum/forks/arrow_glacier/vm/instructions/log.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ def log_n(evm: Evm, num_topics: int) -> None:
The number of topics to be included in the log entry.

"""
if evm.message.is_static:
raise WriteInStaticContext

# STACK
memory_start_index = pop(evm.stack)
size = pop(evm.stack)
Expand All @@ -66,8 +69,6 @@ def log_n(evm: Evm, num_topics: int) -> None:

# OPERATION
evm.memory += b"\x00" * extend_memory.expand_by
if evm.message.is_static:
raise WriteInStaticContext
log_entry = Log(
address=evm.message.current_target,
topics=tuple(topics),
Expand Down
13 changes: 8 additions & 5 deletions src/ethereum/forks/arrow_glacier/vm/instructions/storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,11 @@

from ...state_tracker import get_storage, get_storage_original, set_storage
from .. import Evm
from ..exceptions import OutOfGasError, WriteInStaticContext
from ..exceptions import WriteInStaticContext
from ..gas import (
GasCosts,
charge_gas,
check_gas,
)
from ..stack import pop, push

Expand Down Expand Up @@ -64,11 +65,15 @@ def sstore(evm: Evm) -> None:
The current EVM frame.

"""
if evm.message.is_static:
raise WriteInStaticContext

# STACK
key = pop(evm.stack).to_be_bytes32()
new_value = pop(evm.stack)
if evm.gas_left <= GasCosts.CALL_STIPEND:
raise OutOfGasError

# check we have at least the stipend gas
check_gas(evm, GasCosts.CALL_STIPEND + Uint(1))

tx_state = evm.message.tx_env.state
original_value = get_storage_original(
Expand Down Expand Up @@ -118,8 +123,6 @@ def sstore(evm: Evm) -> None:
)

charge_gas(evm, gas_cost)
if evm.message.is_static:
raise WriteInStaticContext
set_storage(tx_state, evm.message.current_target, key, new_value)

# PROGRAM COUNTER
Expand Down
133 changes: 89 additions & 44 deletions src/ethereum/forks/arrow_glacier/vm/instructions/system.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
calculate_gas_extend_memory,
calculate_message_call_gas,
charge_gas,
check_gas,
max_message_call_gas,
)
from ..memory import memory_read_bytes, memory_write
Expand All @@ -64,14 +65,15 @@ def generic_create(
# if it's not moved inside this method
from ...vm.interpreter import STACK_DEPTH_LIMIT, process_create_message

if evm.message.is_static:
raise WriteInStaticContext

call_data = memory_read_bytes(
evm.memory, memory_start_position, memory_size
)

create_message_gas = max_message_call_gas(Uint(evm.gas_left))
evm.gas_left -= create_message_gas
if evm.message.is_static:
raise WriteInStaticContext
evm.return_data = b""

sender_address = evm.message.current_target
Expand Down Expand Up @@ -333,6 +335,9 @@ def call(evm: Evm) -> None:
memory_output_start_position = pop(evm.stack)
memory_output_size = pop(evm.stack)

if evm.message.is_static and value != U256(0):
raise WriteInStaticContext

# GAS
extend_memory = calculate_gas_extend_memory(
evm.memory,
Expand All @@ -342,32 +347,45 @@ def call(evm: Evm) -> None:
],
)

if to in evm.accessed_addresses:
access_gas_cost = GasCosts.WARM_ACCESS
is_cold_access = to not in evm.accessed_addresses
if is_cold_access:
access_gas_cost = GasCosts.COLD_ACCOUNT_ACCESS
else:
access_gas_cost = GasCosts.WARM_ACCESS

transfer_gas_cost = Uint(0) if value == 0 else GasCosts.CALL_VALUE

# check static gas before state access
check_gas(
evm,
access_gas_cost + transfer_gas_cost + extend_memory.cost,
)

# STATE ACCESS
tx_state = evm.message.tx_env.state
if is_cold_access:
evm.accessed_addresses.add(to)
access_gas_cost = GasCosts.COLD_ACCOUNT_ACCESS

code_address = to

create_gas_cost = GasCosts.NEW_ACCOUNT
if value == 0 or is_account_alive(evm.message.tx_env.state, to):
if value == 0 or is_account_alive(tx_state, to):
create_gas_cost = Uint(0)
transfer_gas_cost = Uint(0) if value == 0 else GasCosts.CALL_VALUE

extra_gas = access_gas_cost + transfer_gas_cost + create_gas_cost

message_call_gas = calculate_message_call_gas(
value,
gas,
Uint(evm.gas_left),
extend_memory.cost,
access_gas_cost + create_gas_cost + transfer_gas_cost,
extra_gas,
)
charge_gas(evm, message_call_gas.cost + extend_memory.cost)
if evm.message.is_static and value != U256(0):
raise WriteInStaticContext

# OPERATION
evm.memory += b"\x00" * extend_memory.expand_by
sender_balance = get_account(
evm.message.tx_env.state, evm.message.current_target
).balance
sender_balance = get_account(tx_state, evm.message.current_target).balance
if sender_balance < value:
push(evm.stack, U256(0))
evm.return_data = b""
Expand Down Expand Up @@ -422,13 +440,25 @@ def callcode(evm: Evm) -> None:
],
)

if code_address in evm.accessed_addresses:
access_gas_cost = GasCosts.WARM_ACCESS
else:
evm.accessed_addresses.add(code_address)
is_cold_access = code_address not in evm.accessed_addresses
if is_cold_access:
access_gas_cost = GasCosts.COLD_ACCOUNT_ACCESS
else:
access_gas_cost = GasCosts.WARM_ACCESS

transfer_gas_cost = Uint(0) if value == 0 else GasCosts.CALL_VALUE

# check static gas before state access
check_gas(
evm,
access_gas_cost + extend_memory.cost + transfer_gas_cost,
)

# STATE ACCESS
tx_state = evm.message.tx_env.state
if is_cold_access:
evm.accessed_addresses.add(code_address)

message_call_gas = calculate_message_call_gas(
value,
gas,
Expand All @@ -440,9 +470,7 @@ def callcode(evm: Evm) -> None:

# OPERATION
evm.memory += b"\x00" * extend_memory.expand_by
sender_balance = get_account(
evm.message.tx_env.state, evm.message.current_target
).balance
sender_balance = get_account(tx_state, evm.message.current_target).balance
if sender_balance < value:
push(evm.stack, U256(0))
evm.return_data = b""
Expand Down Expand Up @@ -477,52 +505,55 @@ def selfdestruct(evm: Evm) -> None:
The current EVM frame.

"""
if evm.message.is_static:
raise WriteInStaticContext

# STACK
beneficiary = to_address_masked(pop(evm.stack))

# GAS
gas_cost = GasCosts.OPCODE_SELFDESTRUCT_BASE
if beneficiary not in evm.accessed_addresses:
evm.accessed_addresses.add(beneficiary)

is_cold_access = beneficiary not in evm.accessed_addresses
if is_cold_access:
gas_cost += GasCosts.COLD_ACCOUNT_ACCESS

# check access gas cost before state access
check_gas(evm, gas_cost)

# STATE ACCESS
tx_state = evm.message.tx_env.state
if is_cold_access:
evm.accessed_addresses.add(beneficiary)

if (
not is_account_alive(evm.message.tx_env.state, beneficiary)
and get_account(
evm.message.tx_env.state, evm.message.current_target
).balance
!= 0
not is_account_alive(tx_state, beneficiary)
and get_account(tx_state, evm.message.current_target).balance != 0
):
gas_cost += GasCosts.OPCODE_SELFDESTRUCT_NEW_ACCOUNT

charge_gas(evm, gas_cost)
if evm.message.is_static:
raise WriteInStaticContext

originator = evm.message.current_target
beneficiary_balance = get_account(
evm.message.tx_env.state, beneficiary
).balance
originator_balance = get_account(
evm.message.tx_env.state, originator
).balance
beneficiary_balance = get_account(tx_state, beneficiary).balance
originator_balance = get_account(tx_state, originator).balance

# First Transfer to beneficiary
set_account_balance(
evm.message.tx_env.state,
tx_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.message.tx_env.state, originator, U256(0))
set_account_balance(tx_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.message.tx_env.state, beneficiary):
if account_exists_and_is_empty(tx_state, beneficiary):
evm.touched_accounts.add(beneficiary)

# HALT the execution
Expand Down Expand Up @@ -559,11 +590,18 @@ def delegatecall(evm: Evm) -> None:
],
)

if code_address in evm.accessed_addresses:
access_gas_cost = GasCosts.WARM_ACCESS
is_cold_access = code_address not in evm.accessed_addresses
if is_cold_access:
access_gas_cost = GasCosts.COLD_ACCOUNT_ACCESS
else:
access_gas_cost = GasCosts.WARM_ACCESS

# check static gas before state access
check_gas(evm, access_gas_cost + extend_memory.cost)

# STATE ACCESS
if is_cold_access:
evm.accessed_addresses.add(code_address)
access_gas_cost = GasCosts.COLD_ACCOUNT_ACCESS

message_call_gas = calculate_message_call_gas(
U256(0),
Expand Down Expand Up @@ -622,11 +660,18 @@ def staticcall(evm: Evm) -> None:
],
)

if to in evm.accessed_addresses:
access_gas_cost = GasCosts.WARM_ACCESS
is_cold_access = to not in evm.accessed_addresses
if is_cold_access:
access_gas_cost = GasCosts.COLD_ACCOUNT_ACCESS
else:
access_gas_cost = GasCosts.WARM_ACCESS

# check static gas before state access
check_gas(evm, access_gas_cost + extend_memory.cost)

# STATE ACCESS
if is_cold_access:
evm.accessed_addresses.add(to)
access_gas_cost = GasCosts.COLD_ACCOUNT_ACCESS

code_address = to

Expand Down
17 changes: 17 additions & 0 deletions src/ethereum/forks/berlin/vm/gas.py
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,23 @@ class MessageCallGas:
sub_call: Uint


def check_gas(evm: Evm, amount: Uint) -> None:
"""
Checks if `amount` gas is available without charging it.
Raises OutOfGasError if insufficient gas.

Parameters
----------
evm :
The current EVM.
amount :
The amount of gas to check.

"""
if evm.gas_left < amount:
raise OutOfGasError


def charge_gas(evm: Evm, amount: Uint) -> None:
"""
Subtracts `amount` from `evm.gas_left`.
Expand Down
5 changes: 3 additions & 2 deletions src/ethereum/forks/berlin/vm/instructions/log.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ def log_n(evm: Evm, num_topics: int) -> None:
The number of topics to be included in the log entry.

"""
if evm.message.is_static:
raise WriteInStaticContext

# STACK
memory_start_index = pop(evm.stack)
size = pop(evm.stack)
Expand All @@ -66,8 +69,6 @@ def log_n(evm: Evm, num_topics: int) -> None:

# OPERATION
evm.memory += b"\x00" * extend_memory.expand_by
if evm.message.is_static:
raise WriteInStaticContext
log_entry = Log(
address=evm.message.current_target,
topics=tuple(topics),
Expand Down
Loading
Loading