Skip to content

Commit 304cf07

Browse files
committed
fix(specs): backport check_gas before state access boundaries from Amsterdam
1 parent 810c184 commit 304cf07

64 files changed

Lines changed: 2510 additions & 1159 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

src/ethereum/forks/arrow_glacier/vm/gas.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,23 @@ class MessageCallGas:
194194
sub_call: Uint
195195

196196

197+
def check_gas(evm: Evm, amount: Uint) -> None:
198+
"""
199+
Checks if `amount` gas is available without charging it.
200+
Raises OutOfGasError if insufficient gas.
201+
202+
Parameters
203+
----------
204+
evm :
205+
The current EVM.
206+
amount :
207+
The amount of gas to check.
208+
209+
"""
210+
if evm.gas_left < amount:
211+
raise OutOfGasError
212+
213+
197214
def charge_gas(evm: Evm, amount: Uint) -> None:
198215
"""
199216
Subtracts `amount` from `evm.gas_left`.

src/ethereum/forks/arrow_glacier/vm/instructions/storage.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,11 @@
1515

1616
from ...state_tracker import get_storage, get_storage_original, set_storage
1717
from .. import Evm
18-
from ..exceptions import OutOfGasError, WriteInStaticContext
18+
from ..exceptions import WriteInStaticContext
1919
from ..gas import (
2020
GasCosts,
2121
charge_gas,
22+
check_gas,
2223
)
2324
from ..stack import pop, push
2425

@@ -64,11 +65,15 @@ def sstore(evm: Evm) -> None:
6465
The current EVM frame.
6566
6667
"""
68+
if evm.message.is_static:
69+
raise WriteInStaticContext
70+
6771
# STACK
6872
key = pop(evm.stack).to_be_bytes32()
6973
new_value = pop(evm.stack)
70-
if evm.gas_left <= GasCosts.CALL_STIPEND:
71-
raise OutOfGasError
74+
75+
# check we have at least the stipend gas
76+
check_gas(evm, GasCosts.CALL_STIPEND + Uint(1))
7277

7378
tx_state = evm.message.tx_env.state
7479
original_value = get_storage_original(
@@ -118,8 +123,6 @@ def sstore(evm: Evm) -> None:
118123
)
119124

120125
charge_gas(evm, gas_cost)
121-
if evm.message.is_static:
122-
raise WriteInStaticContext
123126
set_storage(tx_state, evm.message.current_target, key, new_value)
124127

125128
# PROGRAM COUNTER

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

Lines changed: 89 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
calculate_gas_extend_memory,
4545
calculate_message_call_gas,
4646
charge_gas,
47+
check_gas,
4748
max_message_call_gas,
4849
)
4950
from ..memory import memory_read_bytes, memory_write
@@ -64,14 +65,15 @@ def generic_create(
6465
# if it's not moved inside this method
6566
from ...vm.interpreter import STACK_DEPTH_LIMIT, process_create_message
6667

68+
if evm.message.is_static:
69+
raise WriteInStaticContext
70+
6771
call_data = memory_read_bytes(
6872
evm.memory, memory_start_position, memory_size
6973
)
7074

7175
create_message_gas = max_message_call_gas(Uint(evm.gas_left))
7276
evm.gas_left -= create_message_gas
73-
if evm.message.is_static:
74-
raise WriteInStaticContext
7577
evm.return_data = b""
7678

7779
sender_address = evm.message.current_target
@@ -333,6 +335,9 @@ def call(evm: Evm) -> None:
333335
memory_output_start_position = pop(evm.stack)
334336
memory_output_size = pop(evm.stack)
335337

338+
if evm.message.is_static and value != U256(0):
339+
raise WriteInStaticContext
340+
336341
# GAS
337342
extend_memory = calculate_gas_extend_memory(
338343
evm.memory,
@@ -342,32 +347,45 @@ def call(evm: Evm) -> None:
342347
],
343348
)
344349

345-
if to in evm.accessed_addresses:
346-
access_gas_cost = GasCosts.WARM_ACCESS
350+
is_cold_access = to not in evm.accessed_addresses
351+
if is_cold_access:
352+
access_gas_cost = GasCosts.COLD_ACCOUNT_ACCESS
347353
else:
354+
access_gas_cost = GasCosts.WARM_ACCESS
355+
356+
transfer_gas_cost = Uint(0) if value == 0 else GasCosts.CALL_VALUE
357+
358+
# check static gas before state access
359+
check_gas(
360+
evm,
361+
access_gas_cost + transfer_gas_cost + extend_memory.cost,
362+
)
363+
364+
# STATE ACCESS
365+
tx_state = evm.message.tx_env.state
366+
if is_cold_access:
348367
evm.accessed_addresses.add(to)
349-
access_gas_cost = GasCosts.COLD_ACCOUNT_ACCESS
350368

351369
code_address = to
352370

353371
create_gas_cost = GasCosts.NEW_ACCOUNT
354-
if value == 0 or is_account_alive(evm.message.tx_env.state, to):
372+
if value == 0 or is_account_alive(tx_state, to):
355373
create_gas_cost = Uint(0)
356-
transfer_gas_cost = Uint(0) if value == 0 else GasCosts.CALL_VALUE
374+
375+
extra_gas = access_gas_cost + transfer_gas_cost + create_gas_cost
376+
357377
message_call_gas = calculate_message_call_gas(
358378
value,
359379
gas,
360380
Uint(evm.gas_left),
361381
extend_memory.cost,
362-
access_gas_cost + create_gas_cost + transfer_gas_cost,
382+
extra_gas,
363383
)
364384
charge_gas(evm, message_call_gas.cost + extend_memory.cost)
365-
if evm.message.is_static and value != U256(0):
366-
raise WriteInStaticContext
385+
386+
# OPERATION
367387
evm.memory += b"\x00" * extend_memory.expand_by
368-
sender_balance = get_account(
369-
evm.message.tx_env.state, evm.message.current_target
370-
).balance
388+
sender_balance = get_account(tx_state, evm.message.current_target).balance
371389
if sender_balance < value:
372390
push(evm.stack, U256(0))
373391
evm.return_data = b""
@@ -422,13 +440,25 @@ def callcode(evm: Evm) -> None:
422440
],
423441
)
424442

425-
if code_address in evm.accessed_addresses:
426-
access_gas_cost = GasCosts.WARM_ACCESS
427-
else:
428-
evm.accessed_addresses.add(code_address)
443+
is_cold_access = code_address not in evm.accessed_addresses
444+
if is_cold_access:
429445
access_gas_cost = GasCosts.COLD_ACCOUNT_ACCESS
446+
else:
447+
access_gas_cost = GasCosts.WARM_ACCESS
430448

431449
transfer_gas_cost = Uint(0) if value == 0 else GasCosts.CALL_VALUE
450+
451+
# check static gas before state access
452+
check_gas(
453+
evm,
454+
access_gas_cost + extend_memory.cost + transfer_gas_cost,
455+
)
456+
457+
# STATE ACCESS
458+
tx_state = evm.message.tx_env.state
459+
if is_cold_access:
460+
evm.accessed_addresses.add(code_address)
461+
432462
message_call_gas = calculate_message_call_gas(
433463
value,
434464
gas,
@@ -440,9 +470,7 @@ def callcode(evm: Evm) -> None:
440470

441471
# OPERATION
442472
evm.memory += b"\x00" * extend_memory.expand_by
443-
sender_balance = get_account(
444-
evm.message.tx_env.state, evm.message.current_target
445-
).balance
473+
sender_balance = get_account(tx_state, evm.message.current_target).balance
446474
if sender_balance < value:
447475
push(evm.stack, U256(0))
448476
evm.return_data = b""
@@ -477,52 +505,55 @@ def selfdestruct(evm: Evm) -> None:
477505
The current EVM frame.
478506
479507
"""
508+
if evm.message.is_static:
509+
raise WriteInStaticContext
510+
480511
# STACK
481512
beneficiary = to_address_masked(pop(evm.stack))
482513

483514
# GAS
484515
gas_cost = GasCosts.OPCODE_SELFDESTRUCT_BASE
485-
if beneficiary not in evm.accessed_addresses:
486-
evm.accessed_addresses.add(beneficiary)
516+
517+
is_cold_access = beneficiary not in evm.accessed_addresses
518+
if is_cold_access:
487519
gas_cost += GasCosts.COLD_ACCOUNT_ACCESS
488520

521+
# check access gas cost before state access
522+
check_gas(evm, gas_cost)
523+
524+
# STATE ACCESS
525+
tx_state = evm.message.tx_env.state
526+
if is_cold_access:
527+
evm.accessed_addresses.add(beneficiary)
528+
489529
if (
490-
not is_account_alive(evm.message.tx_env.state, beneficiary)
491-
and get_account(
492-
evm.message.tx_env.state, evm.message.current_target
493-
).balance
494-
!= 0
530+
not is_account_alive(tx_state, beneficiary)
531+
and get_account(tx_state, evm.message.current_target).balance != 0
495532
):
496533
gas_cost += GasCosts.OPCODE_SELFDESTRUCT_NEW_ACCOUNT
497534

498535
charge_gas(evm, gas_cost)
499-
if evm.message.is_static:
500-
raise WriteInStaticContext
501536

502537
originator = evm.message.current_target
503-
beneficiary_balance = get_account(
504-
evm.message.tx_env.state, beneficiary
505-
).balance
506-
originator_balance = get_account(
507-
evm.message.tx_env.state, originator
508-
).balance
538+
beneficiary_balance = get_account(tx_state, beneficiary).balance
539+
originator_balance = get_account(tx_state, originator).balance
509540

510541
# First Transfer to beneficiary
511542
set_account_balance(
512-
evm.message.tx_env.state,
543+
tx_state,
513544
beneficiary,
514545
beneficiary_balance + originator_balance,
515546
)
516547
# Next, Zero the balance of the address being deleted (must come after
517548
# sending to beneficiary in case the contract named itself as the
518549
# beneficiary).
519-
set_account_balance(evm.message.tx_env.state, originator, U256(0))
550+
set_account_balance(tx_state, originator, U256(0))
520551

521552
# register account for deletion
522553
evm.accounts_to_delete.add(originator)
523554

524555
# mark beneficiary as touched
525-
if account_exists_and_is_empty(evm.message.tx_env.state, beneficiary):
556+
if account_exists_and_is_empty(tx_state, beneficiary):
526557
evm.touched_accounts.add(beneficiary)
527558

528559
# HALT the execution
@@ -559,11 +590,18 @@ def delegatecall(evm: Evm) -> None:
559590
],
560591
)
561592

562-
if code_address in evm.accessed_addresses:
563-
access_gas_cost = GasCosts.WARM_ACCESS
593+
is_cold_access = code_address not in evm.accessed_addresses
594+
if is_cold_access:
595+
access_gas_cost = GasCosts.COLD_ACCOUNT_ACCESS
564596
else:
597+
access_gas_cost = GasCosts.WARM_ACCESS
598+
599+
# check static gas before state access
600+
check_gas(evm, access_gas_cost + extend_memory.cost)
601+
602+
# STATE ACCESS
603+
if is_cold_access:
565604
evm.accessed_addresses.add(code_address)
566-
access_gas_cost = GasCosts.COLD_ACCOUNT_ACCESS
567605

568606
message_call_gas = calculate_message_call_gas(
569607
U256(0),
@@ -622,11 +660,18 @@ def staticcall(evm: Evm) -> None:
622660
],
623661
)
624662

625-
if to in evm.accessed_addresses:
626-
access_gas_cost = GasCosts.WARM_ACCESS
663+
is_cold_access = to not in evm.accessed_addresses
664+
if is_cold_access:
665+
access_gas_cost = GasCosts.COLD_ACCOUNT_ACCESS
627666
else:
667+
access_gas_cost = GasCosts.WARM_ACCESS
668+
669+
# check static gas before state access
670+
check_gas(evm, access_gas_cost + extend_memory.cost)
671+
672+
# STATE ACCESS
673+
if is_cold_access:
628674
evm.accessed_addresses.add(to)
629-
access_gas_cost = GasCosts.COLD_ACCOUNT_ACCESS
630675

631676
code_address = to
632677

src/ethereum/forks/berlin/vm/gas.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,23 @@ class MessageCallGas:
194194
sub_call: Uint
195195

196196

197+
def check_gas(evm: Evm, amount: Uint) -> None:
198+
"""
199+
Checks if `amount` gas is available without charging it.
200+
Raises OutOfGasError if insufficient gas.
201+
202+
Parameters
203+
----------
204+
evm :
205+
The current EVM.
206+
amount :
207+
The amount of gas to check.
208+
209+
"""
210+
if evm.gas_left < amount:
211+
raise OutOfGasError
212+
213+
197214
def charge_gas(evm: Evm, amount: Uint) -> None:
198215
"""
199216
Subtracts `amount` from `evm.gas_left`.

src/ethereum/forks/berlin/vm/instructions/storage.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,11 @@
1515

1616
from ...state_tracker import get_storage, get_storage_original, set_storage
1717
from .. import Evm
18-
from ..exceptions import OutOfGasError, WriteInStaticContext
18+
from ..exceptions import WriteInStaticContext
1919
from ..gas import (
2020
GasCosts,
2121
charge_gas,
22+
check_gas,
2223
)
2324
from ..stack import pop, push
2425

@@ -64,11 +65,15 @@ def sstore(evm: Evm) -> None:
6465
The current EVM frame.
6566
6667
"""
68+
if evm.message.is_static:
69+
raise WriteInStaticContext
70+
6771
# STACK
6872
key = pop(evm.stack).to_be_bytes32()
6973
new_value = pop(evm.stack)
70-
if evm.gas_left <= GasCosts.CALL_STIPEND:
71-
raise OutOfGasError
74+
75+
# check we have at least the stipend gas
76+
check_gas(evm, GasCosts.CALL_STIPEND + Uint(1))
7277

7378
tx_state = evm.message.tx_env.state
7479
original_value = get_storage_original(
@@ -118,8 +123,6 @@ def sstore(evm: Evm) -> None:
118123
)
119124

120125
charge_gas(evm, gas_cost)
121-
if evm.message.is_static:
122-
raise WriteInStaticContext
123126
set_storage(tx_state, evm.message.current_target, key, new_value)
124127

125128
# PROGRAM COUNTER

0 commit comments

Comments
 (0)