Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -161,14 +161,12 @@ def test_precompile_warming(
successor = get_transition_fork_successor(fork)

def get_expected_gas(precompile_present: bool, fork: Fork) -> int:
gas_costs = fork.gas_costs()
warm_access_cost = gas_costs.G_WARM_ACCOUNT_ACCESS
cold_access_cost = gas_costs.G_COLD_ACCOUNT_ACCESS
extra_cost = gas_costs.G_BASE * 2 + gas_costs.G_VERY_LOW
if precompile_present:
return warm_access_cost + extra_cost
else:
return cold_access_cost + extra_cost
balance_cost = Op.BALANCE(address_warm=precompile_present).gas_cost(
fork
)
# Overhead: GAS + POP + SUB
overhead_cost = (Op.GAS + Op.POP + Op.SUB).gas_cost(fork)
return balance_cost + overhead_cost

expected_gas_before = get_expected_gas(
precompile_in_predecessor, predecessor
Expand Down
35 changes: 16 additions & 19 deletions tests/berlin/eip2930_access_list/test_acl.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,15 +42,15 @@ def test_account_storage_warm_cold_state(
) -> None:
"""Test type 1 transaction."""
env = Environment()
gas_costs = fork.gas_costs()

storage_reader_contract = pre.deploy_contract(Op.SLOAD(1) + Op.STOP)
# Overhead: PUSH args for CALL (popped_stack_items - 1)
# + GAS opcode + PUSH for SLOAD
overhead_cost = (
gas_costs.G_VERY_LOW
* (Op.CALL.popped_stack_items - 1) # Call stack items
+ gas_costs.G_BASE # Call gas
+ gas_costs.G_VERY_LOW # SLOAD Push
)
Op.PUSH1(0) * (Op.CALL.popped_stack_items - 1)
+ Op.GAS
+ Op.PUSH1(0) # SLOAD push
).gas_cost(fork)
contract_address = pre.deploy_contract(
CodeGasMeasure(
code=Op.CALL(address=storage_reader_contract),
Expand All @@ -59,19 +59,16 @@ def test_account_storage_warm_cold_state(
sstore_key=0,
)
)
expected_gas_cost = 0
access_list_address = Address(0)
access_list_storage_key = Hash(0)
# Expected gas: CALL access cost + SLOAD cost
expected_gas_cost = Op.CALL(address_warm=account_warm).gas_cost(
fork
) + Op.SLOAD(key_warm=storage_key_warm).gas_cost(fork)
if account_warm:
expected_gas_cost += gas_costs.G_WARM_ACCOUNT_ACCESS
access_list_address = storage_reader_contract
else:
expected_gas_cost += gas_costs.G_COLD_ACCOUNT_ACCESS
if storage_key_warm:
expected_gas_cost += gas_costs.G_WARM_SLOAD
access_list_storage_key = Hash(1)
else:
expected_gas_cost += gas_costs.G_COLD_SLOAD

access_lists: List[AccessList] = [
AccessList(
Expand Down Expand Up @@ -288,21 +285,21 @@ def test_repeated_address_acl(
of each access in order to make debugging easier.
"""
sender = pre.fund_eoa()
gsc = fork.gas_costs()

# Cost of pushing SLOAD args
sload_push_cost = (Op.PUSH1(0) * len(Op.SLOAD.kwargs)).gas_cost(fork)

sload0_measure = CodeGasMeasure(
code=Op.SLOAD(0),
overhead_cost=gsc.G_VERY_LOW
* len(Op.SLOAD.kwargs), # Cost of pushing SLOAD args
overhead_cost=sload_push_cost,
extra_stack_items=1, # SLOAD pushes 1 item to the stack
sstore_key=0,
stop=False, # Because it's the first CodeGasMeasure
)

sload1_measure = CodeGasMeasure(
code=Op.SLOAD(1),
overhead_cost=gsc.G_VERY_LOW
* len(Op.SLOAD.kwargs), # Cost of pushing SLOAD args
overhead_cost=sload_push_cost,
extra_stack_items=1, # SLOAD pushes 1 item to the stack
sstore_key=1,
)
Expand All @@ -326,7 +323,7 @@ def test_repeated_address_acl(
],
)

sload_cost = gsc.G_WARM_ACCOUNT_ACCESS
sload_cost = Op.SLOAD(key_warm=True).gas_cost(fork)

state_test(
env=Environment(),
Expand Down
53 changes: 28 additions & 25 deletions tests/frontier/opcodes/test_call.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,21 +31,22 @@ def test_call_large_offset_mstore(
"""
sender = pre.fund_eoa()

gsc = fork.gas_costs()
mem_offset = 128 # arbitrary number

# Cost of pushing args onto the stack (each PUSH costs G_VERY_LOW)
call_push_cost = (Op.PUSH1(0) * len(Op.CALL.kwargs)).gas_cost(fork)
mstore_push_cost = (Op.PUSH1(0) * len(Op.MSTORE.kwargs)).gas_cost(fork)

call_measure = CodeGasMeasure(
code=Op.CALL(gas=0, ret_offset=mem_offset, ret_size=0),
# Cost of pushing CALL args
overhead_cost=gsc.G_VERY_LOW * len(Op.CALL.kwargs),
overhead_cost=call_push_cost,
extra_stack_items=1, # Because CALL pushes 1 item to the stack
sstore_key=0,
stop=False, # Because it's the first CodeGasMeasure
)
mstore_measure = CodeGasMeasure(
code=Op.MSTORE(offset=mem_offset, value=1),
# Cost of pushing MSTORE args
overhead_cost=gsc.G_VERY_LOW * len(Op.MSTORE.kwargs),
overhead_cost=mstore_push_cost,
extra_stack_items=0,
sstore_key=1,
)
Expand All @@ -60,13 +61,10 @@ def test_call_large_offset_mstore(
)

# this call cost is just the address_access_cost
call_cost = gsc.G_COLD_ACCOUNT_ACCESS
call_cost = Op.CALL(address_warm=False).gas_cost(fork)

memory_expansion_gas_calc = fork.memory_expansion_gas_calculator()
# mstore cost: base cost + expansion cost
mstore_cost = gsc.G_MEMORY + memory_expansion_gas_calc(
new_bytes=mem_offset + 1
)
mstore_cost = Op.MSTORE(new_memory_size=mem_offset + 32).gas_cost(fork)
state_test(
env=Environment(),
pre=pre,
Expand Down Expand Up @@ -100,15 +98,17 @@ def test_call_memory_expands_on_early_revert(
"""
sender = pre.fund_eoa()

gsc = fork.gas_costs()
# arbitrary number, greater than memory size to trigger an expansion
ret_size = 128

# Cost of pushing args onto the stack (each PUSH costs G_VERY_LOW)
call_push_cost = (Op.PUSH1(0) * len(Op.CALL.kwargs)).gas_cost(fork)
mstore_push_cost = (Op.PUSH1(0) * len(Op.MSTORE.kwargs)).gas_cost(fork)

call_measure = CodeGasMeasure(
# CALL with value
code=Op.CALL(gas=0, value=100, ret_size=ret_size),
# Cost of pushing CALL args
overhead_cost=gsc.G_VERY_LOW * len(Op.CALL.kwargs),
overhead_cost=call_push_cost,
# Because CALL pushes 1 item to the stack
extra_stack_items=1,
sstore_key=0,
Expand All @@ -118,8 +118,7 @@ def test_call_memory_expands_on_early_revert(
mstore_measure = CodeGasMeasure(
# Low offset for not expanding memory
code=Op.MSTORE(offset=ret_size // 2, value=1),
# Cost of pushing MSTORE args
overhead_cost=gsc.G_VERY_LOW * len(Op.MSTORE.kwargs),
overhead_cost=mstore_push_cost,
extra_stack_items=0,
sstore_key=1,
)
Expand All @@ -136,20 +135,23 @@ def test_call_memory_expands_on_early_revert(
sender=sender,
)

memory_expansion_gas_calc = fork.memory_expansion_gas_calculator()
# call cost:
# address_access_cost+new_acc_cost+memory_expansion_cost+value-stipend
# G_CALL_STIPEND is a threshold check, not a gas cost — keep from gas_costs
gsc = fork.gas_costs()
call_cost = (
gsc.G_COLD_ACCOUNT_ACCESS
+ gsc.G_NEW_ACCOUNT
+ memory_expansion_gas_calc(new_bytes=ret_size)
+ gsc.G_CALL_VALUE
Op.CALL(
address_warm=False,
value_transfer=True,
account_new=True,
new_memory_size=ret_size,
).gas_cost(fork)
- gsc.G_CALL_STIPEND
)

# mstore cost: base cost. No memory expansion cost needed, it was expanded
# on CALL.
mstore_cost = gsc.G_MEMORY
mstore_cost = Op.MSTORE(new_memory_size=0).gas_cost(fork)
state_test(
env=Environment(),
pre=pre,
Expand Down Expand Up @@ -182,13 +184,14 @@ def test_call_large_args_offset_size_zero(
"""
sender = pre.fund_eoa()

gsc = fork.gas_costs()
very_large_offset = 2**100

# Cost of pushing args onto the stack (each PUSH costs G_VERY_LOW)
push_cost = (Op.PUSH1(0) * len(call_opcode.kwargs)).gas_cost(fork)

call_measure = CodeGasMeasure(
code=call_opcode(gas=0, args_offset=very_large_offset, args_size=0),
# Cost of pushing xCALL args
overhead_cost=gsc.G_VERY_LOW * len(call_opcode.kwargs),
overhead_cost=push_cost,
extra_stack_items=1, # Because xCALL pushes 1 item to the stack
sstore_key=0,
)
Expand All @@ -203,7 +206,7 @@ def test_call_large_args_offset_size_zero(
)

# this call cost is just the address_access_cost
call_cost = gsc.G_COLD_ACCOUNT_ACCESS
call_cost = call_opcode(address_warm=False).gas_cost(fork)

state_test(
env=Environment(),
Expand Down
41 changes: 23 additions & 18 deletions tests/frontier/opcodes/test_call_and_callcode_gas_calculation.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,10 +66,12 @@ def callee_init_stack_gas(callee_opcode: Op, fork: Fork) -> int:
"""
if fork < Byzantium:
# all *CALL arguments handled with PUSHes
return len(callee_opcode.kwargs) * 3
return (Op.PUSH1(0) * len(callee_opcode.kwargs)).gas_cost(fork)
else:
# gas argument handled with GAS which is cheaper
return (len(callee_opcode.kwargs) - 1) * 3 + 2
return (
Op.PUSH1(0) * (len(callee_opcode.kwargs) - 1) + Op.GAS
).gas_cost(fork)


@pytest.fixture
Expand All @@ -80,30 +82,33 @@ def sufficient_gas(
Calculate the sufficient gas for the nested call opcode with positive
value transfer.
"""
gas_costs = fork.gas_costs()

cost = 0
is_value_call = callee_opcode in [Op.CALL, Op.CALLCODE]

if fork >= Berlin:
cost += gas_costs.G_COLD_ACCOUNT_ACCESS
metadata: dict = {"address_warm": False}
if is_value_call:
metadata["value_transfer"] = True
metadata["account_new"] = callee_opcode == Op.CALL
cost = callee_opcode(**metadata).gas_cost(fork)
elif Byzantium <= fork < Berlin:
cost += 700 # Pre-Berlin warm call cost
cost = 700 # Pre-Berlin call cost
gas_costs = fork.gas_costs()
if is_value_call:
cost += gas_costs.G_CALL_VALUE
if callee_opcode == Op.CALL:
cost += gas_costs.G_NEW_ACCOUNT
elif fork == Homestead:
cost += 40 # Homestead call cost
cost = 40 # Homestead call cost
cost += 1 # mandatory callee gas allowance
gas_costs = fork.gas_costs()
if is_value_call:
cost += gas_costs.G_CALL_VALUE
if callee_opcode == Op.CALL:
cost += gas_costs.G_NEW_ACCOUNT
else:
raise Exception("Only forks Homestead and >=Byzantium supported")

is_value_call = callee_opcode in [Op.CALL, Op.CALLCODE]
if is_value_call:
cost += gas_costs.G_CALL_VALUE

if callee_opcode == Op.CALL:
cost += gas_costs.G_NEW_ACCOUNT

sufficient = callee_init_stack_gas + cost

return sufficient
return callee_init_stack_gas + cost


@pytest.fixture
Expand Down
Loading
Loading