Skip to content
Closed
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
208 changes: 112 additions & 96 deletions src/ethereum/forks/amsterdam/vm/instructions/system.py
Original file line number Diff line number Diff line change
Expand Up @@ -298,15 +298,10 @@ def generic_call(
"""
Perform the core logic of the `CALL*` family of opcodes.
"""
from ...vm.interpreter import STACK_DEPTH_LIMIT, process_message
from ...vm.interpreter import process_message

evm.return_data = b""

if evm.message.depth + Uint(1) > STACK_DEPTH_LIMIT:
evm.gas_left += gas
push(evm.stack, U256(0))
return

tx_state = evm.message.tx_env.state
code_hash = get_account(tx_state, code_address).code_hash
code = get_code(tx_state, code_hash)
Expand Down Expand Up @@ -386,37 +381,45 @@ def call(evm: Evm) -> None:
)

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,
)
# Pre-state extra gas (warm-equivalent access cost only; cold surcharge
# is deferred to post-state).
extra_gas = GasCosts.WARM_ACCESS + transfer_gas_cost
check_gas(evm, extra_gas + extend_memory.cost)
Comment on lines +388 to +389

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe I'm missing something but I don't quite understand this charge. Couldn't we just check_gas(evm, extend_memory.cost) and immediately if evm.message.depth + Uint(1) > STACK_DEPTH_LIMIT or (value > U256(0) and sender_balance < value), and charge no warm access?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Answering my own comment: With bare check_gas(evm, extend_memory.cost), CALL with insufficient balance would be cheaper to expand the memory (full of zeros) than MSTORE.


# Pre-state preconditions: stack depth and (if value > 0) sender balance.
from ...vm.interpreter import STACK_DEPTH_LIMIT

# STATE ACCESS
tx_state = evm.message.tx_env.state
sender_balance = get_account(tx_state, evm.message.current_target).balance
if evm.message.depth + Uint(1) > STACK_DEPTH_LIMIT or (
value > U256(0) and sender_balance < value
):
charge_gas(evm, extra_gas + extend_memory.cost)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A call that does not have enough balance to send value, ends up paying only the warm cost to access the target account, even if it is cold.
In that case, the target account ends up in BAL but is never added to the access list. If we are going for a "BAL is a strict subset of access list" assumption, this scenario violates it.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah right now it looks like we won't go for this change anyway

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nerolation Is there consensus on a final shape for this?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the default was not changing and clients now had 2-3 weeks to chime in. I'll let clients take the shot on this with the default closing this PR.

evm.memory += b"\x00" * extend_memory.expand_by
push(evm.stack, U256(0))
evm.return_data = b""
evm.pc += Uint(1)
return

# Post-state: cold surcharge paired with warming and BAL insertion.
if is_cold_access:
extra_gas += GasCosts.COLD_ACCOUNT_ACCESS - GasCosts.WARM_ACCESS
check_gas(evm, extra_gas + extend_memory.cost)
evm.accessed_addresses.add(to)

create_gas_cost = GasCosts.NEW_ACCOUNT
if value == 0 or is_account_alive(tx_state, to):
create_gas_cost = Uint(0)
if value > U256(0) and not is_account_alive(tx_state, to):
extra_gas += GasCosts.NEW_ACCOUNT
check_gas(evm, extra_gas + extend_memory.cost)

extra_gas = access_gas_cost + transfer_gas_cost + create_gas_cost
(
is_delegated,
code_address,
delegation_access_cost,
) = calculate_delegation_cost(evm, to)

if is_delegated:
# check enough gas for delegation access
extra_gas += delegation_access_cost
check_gas(evm, extra_gas + extend_memory.cost)
if code_address not in evm.accessed_addresses:
Expand All @@ -433,27 +436,21 @@ def call(evm: Evm) -> None:

# OPERATION
evm.memory += b"\x00" * extend_memory.expand_by
sender_balance = get_account(tx_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.sub_call
else:
generic_call(
evm,
message_call_gas.sub_call,
value,
evm.message.current_target,
to,
code_address,
True,
False,
memory_input_start_position,
memory_input_size,
memory_output_start_position,
memory_output_size,
is_delegated,
)
generic_call(
evm,
message_call_gas.sub_call,
value,
evm.message.current_target,
to,
code_address,
True,
False,
memory_input_start_position,
memory_input_size,
memory_output_start_position,
memory_output_size,
is_delegated,
)

# PROGRAM COUNTER
evm.pc += Uint(1)
Expand Down Expand Up @@ -490,33 +487,41 @@ def callcode(evm: Evm) -> None:
)

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,
)
# Pre-state extra gas (warm-equivalent access cost only; cold surcharge
# is deferred to post-state).
extra_gas = GasCosts.WARM_ACCESS + transfer_gas_cost
check_gas(evm, extra_gas + extend_memory.cost)

# Pre-state preconditions: stack depth and (if value > 0) sender balance.
from ...vm.interpreter import STACK_DEPTH_LIMIT

# STATE ACCESS
tx_state = evm.message.tx_env.state
sender_balance = get_account(tx_state, evm.message.current_target).balance
if evm.message.depth + Uint(1) > STACK_DEPTH_LIMIT or (
value > U256(0) and sender_balance < value
):
charge_gas(evm, extra_gas + extend_memory.cost)
evm.memory += b"\x00" * extend_memory.expand_by
push(evm.stack, U256(0))
evm.return_data = b""
evm.pc += Uint(1)
return

# Post-state: cold surcharge paired with warming and BAL insertion.
if is_cold_access:
extra_gas += GasCosts.COLD_ACCOUNT_ACCESS - GasCosts.WARM_ACCESS
check_gas(evm, extra_gas + extend_memory.cost)
evm.accessed_addresses.add(code_address)

extra_gas = access_gas_cost + transfer_gas_cost
(
is_delegated,
code_address,
delegation_access_cost,
) = calculate_delegation_cost(evm, code_address)

if is_delegated:
# check enough gas for delegation access
extra_gas += delegation_access_cost
check_gas(evm, extra_gas + extend_memory.cost)
if code_address not in evm.accessed_addresses:
Expand All @@ -533,28 +538,21 @@ def callcode(evm: Evm) -> None:

# OPERATION
evm.memory += b"\x00" * extend_memory.expand_by
sender_balance = get_account(tx_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.sub_call
else:
generic_call(
evm,
message_call_gas.sub_call,
value,
evm.message.current_target,
to,
code_address,
True,
False,
memory_input_start_position,
memory_input_size,
memory_output_start_position,
memory_output_size,
is_delegated,
)
generic_call(
evm,
message_call_gas.sub_call,
value,
evm.message.current_target,
to,
code_address,
True,
False,
memory_input_start_position,
memory_input_size,
memory_output_start_position,
memory_output_size,
is_delegated,
)

# PROGRAM COUNTER
evm.pc += Uint(1)
Expand Down Expand Up @@ -652,27 +650,36 @@ def delegatecall(evm: Evm) -> None:
)

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)
# Pre-state extra gas (warm-equivalent access cost only; cold surcharge
# is deferred to post-state).
extra_gas = GasCosts.WARM_ACCESS
check_gas(evm, extra_gas + extend_memory.cost)

# STATE ACCESS
# Pre-state precondition: stack depth.
from ...vm.interpreter import STACK_DEPTH_LIMIT

if evm.message.depth + Uint(1) > STACK_DEPTH_LIMIT:
charge_gas(evm, extra_gas + extend_memory.cost)
evm.memory += b"\x00" * extend_memory.expand_by
push(evm.stack, U256(0))
evm.return_data = b""
evm.pc += Uint(1)
return

# Post-state: cold surcharge paired with warming and BAL insertion.
if is_cold_access:
extra_gas += GasCosts.COLD_ACCOUNT_ACCESS - GasCosts.WARM_ACCESS
check_gas(evm, extra_gas + extend_memory.cost)
evm.accessed_addresses.add(code_address)

extra_gas = access_gas_cost
(
is_delegated,
code_address,
delegation_access_cost,
) = calculate_delegation_cost(evm, code_address)

if is_delegated:
# check enough gas for delegation access
extra_gas += delegation_access_cost
check_gas(evm, extra_gas + extend_memory.cost)
if code_address not in evm.accessed_addresses:
Expand Down Expand Up @@ -737,27 +744,36 @@ def staticcall(evm: Evm) -> None:
)

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)
# Pre-state extra gas (warm-equivalent access cost only; cold surcharge
# is deferred to post-state).
extra_gas = GasCosts.WARM_ACCESS
check_gas(evm, extra_gas + extend_memory.cost)

# STATE ACCESS
# Pre-state precondition: stack depth.
from ...vm.interpreter import STACK_DEPTH_LIMIT

if evm.message.depth + Uint(1) > STACK_DEPTH_LIMIT:
charge_gas(evm, extra_gas + extend_memory.cost)
evm.memory += b"\x00" * extend_memory.expand_by
push(evm.stack, U256(0))
evm.return_data = b""
evm.pc += Uint(1)
return

# Post-state: cold surcharge paired with warming and BAL insertion.
if is_cold_access:
extra_gas += GasCosts.COLD_ACCOUNT_ACCESS - GasCosts.WARM_ACCESS
check_gas(evm, extra_gas + extend_memory.cost)
evm.accessed_addresses.add(to)

extra_gas = access_gas_cost
(
is_delegated,
code_address,
delegation_access_cost,
) = calculate_delegation_cost(evm, to)

if is_delegated:
# check enough gas for delegation access
extra_gas += delegation_access_cost
check_gas(evm, extra_gas + extend_memory.cost)
if code_address not in evm.accessed_addresses:
Expand Down
Loading