Skip to content

Commit e4e70ad

Browse files
committed
feat(amsterdam): defer cold surcharge and warming to after CALL preconditions
1 parent abf6ff6 commit e4e70ad

1 file changed

Lines changed: 112 additions & 96 deletions

File tree

  • src/ethereum/forks/amsterdam/vm/instructions

src/ethereum/forks/amsterdam/vm/instructions/system.py

Lines changed: 112 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -298,15 +298,10 @@ def generic_call(
298298
"""
299299
Perform the core logic of the `CALL*` family of opcodes.
300300
"""
301-
from ...vm.interpreter import STACK_DEPTH_LIMIT, process_message
301+
from ...vm.interpreter import process_message
302302

303303
evm.return_data = b""
304304

305-
if evm.message.depth + Uint(1) > STACK_DEPTH_LIMIT:
306-
evm.gas_left += gas
307-
push(evm.stack, U256(0))
308-
return
309-
310305
tx_state = evm.message.tx_env.state
311306
code_hash = get_account(tx_state, code_address).code_hash
312307
code = get_code(tx_state, code_hash)
@@ -386,37 +381,45 @@ def call(evm: Evm) -> None:
386381
)
387382

388383
is_cold_access = to not in evm.accessed_addresses
389-
if is_cold_access:
390-
access_gas_cost = GasCosts.COLD_ACCOUNT_ACCESS
391-
else:
392-
access_gas_cost = GasCosts.WARM_ACCESS
393-
394384
transfer_gas_cost = Uint(0) if value == 0 else GasCosts.CALL_VALUE
395385

396-
# check static gas before state access
397-
check_gas(
398-
evm,
399-
access_gas_cost + transfer_gas_cost + extend_memory.cost,
400-
)
386+
# Pre-state extra gas (warm-equivalent access cost only; cold surcharge
387+
# is deferred to post-state).
388+
extra_gas = GasCosts.WARM_ACCESS + transfer_gas_cost
389+
check_gas(evm, extra_gas + extend_memory.cost)
390+
391+
# Pre-state preconditions: stack depth and (if value > 0) sender balance.
392+
from ...vm.interpreter import STACK_DEPTH_LIMIT
401393

402-
# STATE ACCESS
403394
tx_state = evm.message.tx_env.state
395+
sender_balance = get_account(tx_state, evm.message.current_target).balance
396+
if evm.message.depth + Uint(1) > STACK_DEPTH_LIMIT or (
397+
value > U256(0) and sender_balance < value
398+
):
399+
charge_gas(evm, extra_gas + extend_memory.cost)
400+
evm.memory += b"\x00" * extend_memory.expand_by
401+
push(evm.stack, U256(0))
402+
evm.return_data = b""
403+
evm.pc += Uint(1)
404+
return
405+
406+
# Post-state: cold surcharge paired with warming and BAL insertion.
404407
if is_cold_access:
408+
extra_gas += GasCosts.COLD_ACCOUNT_ACCESS - GasCosts.WARM_ACCESS
409+
check_gas(evm, extra_gas + extend_memory.cost)
405410
evm.accessed_addresses.add(to)
406411

407-
create_gas_cost = GasCosts.NEW_ACCOUNT
408-
if value == 0 or is_account_alive(tx_state, to):
409-
create_gas_cost = Uint(0)
412+
if value > U256(0) and not is_account_alive(tx_state, to):
413+
extra_gas += GasCosts.NEW_ACCOUNT
414+
check_gas(evm, extra_gas + extend_memory.cost)
410415

411-
extra_gas = access_gas_cost + transfer_gas_cost + create_gas_cost
412416
(
413417
is_delegated,
414418
code_address,
415419
delegation_access_cost,
416420
) = calculate_delegation_cost(evm, to)
417421

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

434437
# OPERATION
435438
evm.memory += b"\x00" * extend_memory.expand_by
436-
sender_balance = get_account(tx_state, evm.message.current_target).balance
437-
if sender_balance < value:
438-
push(evm.stack, U256(0))
439-
evm.return_data = b""
440-
evm.gas_left += message_call_gas.sub_call
441-
else:
442-
generic_call(
443-
evm,
444-
message_call_gas.sub_call,
445-
value,
446-
evm.message.current_target,
447-
to,
448-
code_address,
449-
True,
450-
False,
451-
memory_input_start_position,
452-
memory_input_size,
453-
memory_output_start_position,
454-
memory_output_size,
455-
is_delegated,
456-
)
439+
generic_call(
440+
evm,
441+
message_call_gas.sub_call,
442+
value,
443+
evm.message.current_target,
444+
to,
445+
code_address,
446+
True,
447+
False,
448+
memory_input_start_position,
449+
memory_input_size,
450+
memory_output_start_position,
451+
memory_output_size,
452+
is_delegated,
453+
)
457454

458455
# PROGRAM COUNTER
459456
evm.pc += Uint(1)
@@ -490,33 +487,41 @@ def callcode(evm: Evm) -> None:
490487
)
491488

492489
is_cold_access = code_address not in evm.accessed_addresses
493-
if is_cold_access:
494-
access_gas_cost = GasCosts.COLD_ACCOUNT_ACCESS
495-
else:
496-
access_gas_cost = GasCosts.WARM_ACCESS
497-
498490
transfer_gas_cost = Uint(0) if value == 0 else GasCosts.CALL_VALUE
499491

500-
# check static gas before state access
501-
check_gas(
502-
evm,
503-
access_gas_cost + extend_memory.cost + transfer_gas_cost,
504-
)
492+
# Pre-state extra gas (warm-equivalent access cost only; cold surcharge
493+
# is deferred to post-state).
494+
extra_gas = GasCosts.WARM_ACCESS + transfer_gas_cost
495+
check_gas(evm, extra_gas + extend_memory.cost)
496+
497+
# Pre-state preconditions: stack depth and (if value > 0) sender balance.
498+
from ...vm.interpreter import STACK_DEPTH_LIMIT
505499

506-
# STATE ACCESS
507500
tx_state = evm.message.tx_env.state
501+
sender_balance = get_account(tx_state, evm.message.current_target).balance
502+
if evm.message.depth + Uint(1) > STACK_DEPTH_LIMIT or (
503+
value > U256(0) and sender_balance < value
504+
):
505+
charge_gas(evm, extra_gas + extend_memory.cost)
506+
evm.memory += b"\x00" * extend_memory.expand_by
507+
push(evm.stack, U256(0))
508+
evm.return_data = b""
509+
evm.pc += Uint(1)
510+
return
511+
512+
# Post-state: cold surcharge paired with warming and BAL insertion.
508513
if is_cold_access:
514+
extra_gas += GasCosts.COLD_ACCOUNT_ACCESS - GasCosts.WARM_ACCESS
515+
check_gas(evm, extra_gas + extend_memory.cost)
509516
evm.accessed_addresses.add(code_address)
510517

511-
extra_gas = access_gas_cost + transfer_gas_cost
512518
(
513519
is_delegated,
514520
code_address,
515521
delegation_access_cost,
516522
) = calculate_delegation_cost(evm, code_address)
517523

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

534539
# OPERATION
535540
evm.memory += b"\x00" * extend_memory.expand_by
536-
sender_balance = get_account(tx_state, evm.message.current_target).balance
537-
538-
if sender_balance < value:
539-
push(evm.stack, U256(0))
540-
evm.return_data = b""
541-
evm.gas_left += message_call_gas.sub_call
542-
else:
543-
generic_call(
544-
evm,
545-
message_call_gas.sub_call,
546-
value,
547-
evm.message.current_target,
548-
to,
549-
code_address,
550-
True,
551-
False,
552-
memory_input_start_position,
553-
memory_input_size,
554-
memory_output_start_position,
555-
memory_output_size,
556-
is_delegated,
557-
)
541+
generic_call(
542+
evm,
543+
message_call_gas.sub_call,
544+
value,
545+
evm.message.current_target,
546+
to,
547+
code_address,
548+
True,
549+
False,
550+
memory_input_start_position,
551+
memory_input_size,
552+
memory_output_start_position,
553+
memory_output_size,
554+
is_delegated,
555+
)
558556

559557
# PROGRAM COUNTER
560558
evm.pc += Uint(1)
@@ -652,27 +650,36 @@ def delegatecall(evm: Evm) -> None:
652650
)
653651

654652
is_cold_access = code_address not in evm.accessed_addresses
655-
if is_cold_access:
656-
access_gas_cost = GasCosts.COLD_ACCOUNT_ACCESS
657-
else:
658-
access_gas_cost = GasCosts.WARM_ACCESS
659653

660-
# check static gas before state access
661-
check_gas(evm, access_gas_cost + extend_memory.cost)
654+
# Pre-state extra gas (warm-equivalent access cost only; cold surcharge
655+
# is deferred to post-state).
656+
extra_gas = GasCosts.WARM_ACCESS
657+
check_gas(evm, extra_gas + extend_memory.cost)
662658

663-
# STATE ACCESS
659+
# Pre-state precondition: stack depth.
660+
from ...vm.interpreter import STACK_DEPTH_LIMIT
661+
662+
if evm.message.depth + Uint(1) > STACK_DEPTH_LIMIT:
663+
charge_gas(evm, extra_gas + extend_memory.cost)
664+
evm.memory += b"\x00" * extend_memory.expand_by
665+
push(evm.stack, U256(0))
666+
evm.return_data = b""
667+
evm.pc += Uint(1)
668+
return
669+
670+
# Post-state: cold surcharge paired with warming and BAL insertion.
664671
if is_cold_access:
672+
extra_gas += GasCosts.COLD_ACCOUNT_ACCESS - GasCosts.WARM_ACCESS
673+
check_gas(evm, extra_gas + extend_memory.cost)
665674
evm.accessed_addresses.add(code_address)
666675

667-
extra_gas = access_gas_cost
668676
(
669677
is_delegated,
670678
code_address,
671679
delegation_access_cost,
672680
) = calculate_delegation_cost(evm, code_address)
673681

674682
if is_delegated:
675-
# check enough gas for delegation access
676683
extra_gas += delegation_access_cost
677684
check_gas(evm, extra_gas + extend_memory.cost)
678685
if code_address not in evm.accessed_addresses:
@@ -737,27 +744,36 @@ def staticcall(evm: Evm) -> None:
737744
)
738745

739746
is_cold_access = to not in evm.accessed_addresses
740-
if is_cold_access:
741-
access_gas_cost = GasCosts.COLD_ACCOUNT_ACCESS
742-
else:
743-
access_gas_cost = GasCosts.WARM_ACCESS
744747

745-
# check static gas before state access
746-
check_gas(evm, access_gas_cost + extend_memory.cost)
748+
# Pre-state extra gas (warm-equivalent access cost only; cold surcharge
749+
# is deferred to post-state).
750+
extra_gas = GasCosts.WARM_ACCESS
751+
check_gas(evm, extra_gas + extend_memory.cost)
747752

748-
# STATE ACCESS
753+
# Pre-state precondition: stack depth.
754+
from ...vm.interpreter import STACK_DEPTH_LIMIT
755+
756+
if evm.message.depth + Uint(1) > STACK_DEPTH_LIMIT:
757+
charge_gas(evm, extra_gas + extend_memory.cost)
758+
evm.memory += b"\x00" * extend_memory.expand_by
759+
push(evm.stack, U256(0))
760+
evm.return_data = b""
761+
evm.pc += Uint(1)
762+
return
763+
764+
# Post-state: cold surcharge paired with warming and BAL insertion.
749765
if is_cold_access:
766+
extra_gas += GasCosts.COLD_ACCOUNT_ACCESS - GasCosts.WARM_ACCESS
767+
check_gas(evm, extra_gas + extend_memory.cost)
750768
evm.accessed_addresses.add(to)
751769

752-
extra_gas = access_gas_cost
753770
(
754771
is_delegated,
755772
code_address,
756773
delegation_access_cost,
757774
) = calculate_delegation_cost(evm, to)
758775

759776
if is_delegated:
760-
# check enough gas for delegation access
761777
extra_gas += delegation_access_cost
762778
check_gas(evm, extra_gas + extend_memory.cost)
763779
if code_address not in evm.accessed_addresses:

0 commit comments

Comments
 (0)