Skip to content

Commit 5730621

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

1 file changed

Lines changed: 114 additions & 96 deletions

File tree

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

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

Lines changed: 114 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,47 @@ 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)
401390

402-
# STATE ACCESS
391+
# Pre-state preconditions: stack depth and (if value > 0) sender balance.
392+
from ...vm.interpreter import STACK_DEPTH_LIMIT
403393
tx_state = evm.message.tx_env.state
394+
sender_balance = get_account(
395+
tx_state, evm.message.current_target
396+
).balance
397+
if (
398+
evm.message.depth + Uint(1) > STACK_DEPTH_LIMIT
399+
or (value > 0 and sender_balance < value)
400+
):
401+
charge_gas(evm, extra_gas + extend_memory.cost)
402+
evm.memory += b"\x00" * extend_memory.expand_by
403+
push(evm.stack, U256(0))
404+
evm.return_data = b""
405+
evm.pc += Uint(1)
406+
return
407+
408+
# Post-state: cold surcharge paired with warming and BAL insertion.
404409
if is_cold_access:
410+
extra_gas += GasCosts.COLD_ACCOUNT_ACCESS - GasCosts.WARM_ACCESS
411+
check_gas(evm, extra_gas + extend_memory.cost)
405412
evm.accessed_addresses.add(to)
406413

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

411-
extra_gas = access_gas_cost + transfer_gas_cost + create_gas_cost
412418
(
413419
is_delegated,
414420
code_address,
415421
delegation_access_cost,
416422
) = calculate_delegation_cost(evm, to)
417423

418424
if is_delegated:
419-
# check enough gas for delegation access
420425
extra_gas += delegation_access_cost
421426
check_gas(evm, extra_gas + extend_memory.cost)
422427
if code_address not in evm.accessed_addresses:
@@ -433,27 +438,21 @@ def call(evm: Evm) -> None:
433438

434439
# OPERATION
435440
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-
)
441+
generic_call(
442+
evm,
443+
message_call_gas.sub_call,
444+
value,
445+
evm.message.current_target,
446+
to,
447+
code_address,
448+
True,
449+
False,
450+
memory_input_start_position,
451+
memory_input_size,
452+
memory_output_start_position,
453+
memory_output_size,
454+
is_delegated,
455+
)
457456

458457
# PROGRAM COUNTER
459458
evm.pc += Uint(1)
@@ -490,33 +489,43 @@ def callcode(evm: Evm) -> None:
490489
)
491490

492491
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-
498492
transfer_gas_cost = Uint(0) if value == 0 else GasCosts.CALL_VALUE
499493

500-
# check static gas before state access
501-
check_gas(
502-
evm,
503-
access_gas_cost + extend_memory.cost + transfer_gas_cost,
504-
)
494+
# Pre-state extra gas (warm-equivalent access cost only; cold surcharge
495+
# is deferred to post-state).
496+
extra_gas = GasCosts.WARM_ACCESS + transfer_gas_cost
497+
check_gas(evm, extra_gas + extend_memory.cost)
505498

506-
# STATE ACCESS
499+
# Pre-state preconditions: stack depth and (if value > 0) sender balance.
500+
from ...vm.interpreter import STACK_DEPTH_LIMIT
507501
tx_state = evm.message.tx_env.state
502+
sender_balance = get_account(
503+
tx_state, evm.message.current_target
504+
).balance
505+
if (
506+
evm.message.depth + Uint(1) > STACK_DEPTH_LIMIT
507+
or (value > 0 and sender_balance < value)
508+
):
509+
charge_gas(evm, extra_gas + extend_memory.cost)
510+
evm.memory += b"\x00" * extend_memory.expand_by
511+
push(evm.stack, U256(0))
512+
evm.return_data = b""
513+
evm.pc += Uint(1)
514+
return
515+
516+
# Post-state: cold surcharge paired with warming and BAL insertion.
508517
if is_cold_access:
518+
extra_gas += GasCosts.COLD_ACCOUNT_ACCESS - GasCosts.WARM_ACCESS
519+
check_gas(evm, extra_gas + extend_memory.cost)
509520
evm.accessed_addresses.add(code_address)
510521

511-
extra_gas = access_gas_cost + transfer_gas_cost
512522
(
513523
is_delegated,
514524
code_address,
515525
delegation_access_cost,
516526
) = calculate_delegation_cost(evm, code_address)
517527

518528
if is_delegated:
519-
# check enough gas for delegation access
520529
extra_gas += delegation_access_cost
521530
check_gas(evm, extra_gas + extend_memory.cost)
522531
if code_address not in evm.accessed_addresses:
@@ -533,28 +542,21 @@ def callcode(evm: Evm) -> None:
533542

534543
# OPERATION
535544
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-
)
545+
generic_call(
546+
evm,
547+
message_call_gas.sub_call,
548+
value,
549+
evm.message.current_target,
550+
to,
551+
code_address,
552+
True,
553+
False,
554+
memory_input_start_position,
555+
memory_input_size,
556+
memory_output_start_position,
557+
memory_output_size,
558+
is_delegated,
559+
)
558560

559561
# PROGRAM COUNTER
560562
evm.pc += Uint(1)
@@ -652,27 +654,35 @@ def delegatecall(evm: Evm) -> None:
652654
)
653655

654656
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
659657

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

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

667-
extra_gas = access_gas_cost
668679
(
669680
is_delegated,
670681
code_address,
671682
delegation_access_cost,
672683
) = calculate_delegation_cost(evm, code_address)
673684

674685
if is_delegated:
675-
# check enough gas for delegation access
676686
extra_gas += delegation_access_cost
677687
check_gas(evm, extra_gas + extend_memory.cost)
678688
if code_address not in evm.accessed_addresses:
@@ -737,27 +747,35 @@ def staticcall(evm: Evm) -> None:
737747
)
738748

739749
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
744750

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

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

752-
extra_gas = access_gas_cost
753772
(
754773
is_delegated,
755774
code_address,
756775
delegation_access_cost,
757776
) = calculate_delegation_cost(evm, to)
758777

759778
if is_delegated:
760-
# check enough gas for delegation access
761779
extra_gas += delegation_access_cost
762780
check_gas(evm, extra_gas + extend_memory.cost)
763781
if code_address not in evm.accessed_addresses:

0 commit comments

Comments
 (0)