Skip to content

Commit

Permalink
clarify gas prices
Browse files Browse the repository at this point in the history
  • Loading branch information
winsvega committed Dec 13, 2024
1 parent bae9623 commit b01c835
Show file tree
Hide file tree
Showing 2 changed files with 109 additions and 45 deletions.
2 changes: 2 additions & 0 deletions src/ethereum_test_forks/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
ParisToShanghaiAtTime15k,
ShanghaiToCancunAtTime15k,
)
from .gas_costs import GasCosts
from .helpers import (
InvalidForkError,
forks_from,
Expand Down Expand Up @@ -83,4 +84,5 @@
"get_last_descendants",
"transition_fork_from_to",
"transition_fork_to",
"GasCosts",
]
152 changes: 107 additions & 45 deletions tests/prague/eip7702_set_code_tx/test_set_code_txs_2.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

import pytest

from ethereum_test_forks import Fork, GasCosts
from ethereum_test_tools import (
AccessList,
Account,
Expand Down Expand Up @@ -505,97 +506,156 @@ class AccessListCall(Enum):
"""Add addresses to access list"""

NONE = 1
NORMAL_ONLY = 2
POINTER_ONLY = 3
BOTH = 4
IN_NORMAL_TX_ONLY = 2
IN_POINTER_TX_ONLY = 3
IN_BOTH_TX = 4


class PointerDefinition(Enum):
"""Define pointer in transactions"""

SEPARATE = 1
IN_TX1_ONLY = 2
IN_TX2_ONLY = 3
IN_NORMAL_TX_ONLY = 2
IN_POINTER_TX_ONLY = 3
IN_BOTH_TX = 4


class AccessListTo(Enum):
"""Define access list to"""

POINTER_ADDRESS = 1
CONTRACT_ADDRESS = 2


@pytest.mark.parametrize(
"access_list_rule",
[
AccessListCall.NONE,
AccessListCall.BOTH,
AccessListCall.NORMAL_ONLY,
AccessListCall.POINTER_ONLY,
AccessListCall.IN_BOTH_TX,
AccessListCall.IN_NORMAL_TX_ONLY,
AccessListCall.IN_POINTER_TX_ONLY,
],
)
@pytest.mark.parametrize(
"pointer_definition",
[
PointerDefinition.SEPARATE,
PointerDefinition.IN_BOTH_TX,
PointerDefinition.IN_TX1_ONLY,
PointerDefinition.IN_TX2_ONLY,
PointerDefinition.IN_NORMAL_TX_ONLY,
PointerDefinition.IN_POINTER_TX_ONLY,
],
)
@pytest.mark.parametrize(
"access_list_to",
[AccessListTo.POINTER_ADDRESS, AccessListTo.CONTRACT_ADDRESS],
)
@pytest.mark.valid_from("Prague")
def test_gas_diff_pointer_vs_direct_call(
blockchain_test: BlockchainTestFiller,
pre: Alloc,
fork: Fork,
access_list_rule: AccessListCall,
pointer_definition: PointerDefinition,
access_list_to: AccessListTo,
):
"""
Check the gas difference when calling the contract directly vs as a pointer
Combine with AccessList and AuthTuple gas reductions scenarios
TODO: the test uses direct gas values, which can be replaced with opcode gas price(fork)
in the future. direct gas values are not good to use in tests, but sometimes we need it
"""
env = Environment()

sender = pre.fund_eoa()
pointer_a = pre.fund_eoa()
call_worked = 1
gas_costs: GasCosts = fork.gas_costs()

opcodes_price = 42
G_CALL_OPCODE: int = 100
direct_call_gas: int = (
# 20_000 + 2_600 + 2_100 + 100 + 42 = 24842
gas_costs.G_STORAGE_SET
+ (
# access account price
# If storage and account is declared in access list then discount
gas_costs.G_WARM_ACCOUNT_ACCESS + gas_costs.G_WARM_SLOAD
if access_list_rule in [AccessListCall.IN_NORMAL_TX_ONLY, AccessListCall.IN_BOTH_TX]
else gas_costs.G_COLD_ACCOUNT_ACCESS + gas_costs.G_COLD_SLOAD
)
+ G_CALL_OPCODE
+ opcodes_price
)

pointer_call_gas: int = (
# sstore + addr + addr + sload + call + op
# no access list, no pointer, all accesses are hot
# 20_000 + 2_600 * 2 + 2_100 + 100 + 42 = 27_442
#
# access list for pointer, pointer is set
# additional 2_600 charged for access of contract
# 20_000 + 100 + 2_600 + 100 + 100 + 42 = 22_942
#
# no access list, pointer is set
# pointer access is hot, sload and contract are hot
# 20_000 + 100 + 2_600 + 2_100 + 100 + 42 = 24_942
#
# access list for contract, pointer is set
# contract call is hot, pointer call is call because pointer is set
# only sload is hot because access list is for contract
# 20_000 + 100 + 100 + 2100 + 100 + 42 = 22_442
gas_costs.G_STORAGE_SET
# pointer address access
+ (
gas_costs.G_WARM_ACCOUNT_ACCESS
if (
pointer_definition
in [PointerDefinition.IN_BOTH_TX, PointerDefinition.IN_POINTER_TX_ONLY]
or access_list_rule
in [AccessListCall.IN_BOTH_TX, AccessListCall.IN_POINTER_TX_ONLY]
and access_list_to == AccessListTo.POINTER_ADDRESS
)
else gas_costs.G_COLD_ACCOUNT_ACCESS
)
# storage access
+ (
gas_costs.G_WARM_SLOAD
if (
access_list_rule in [AccessListCall.IN_BOTH_TX, AccessListCall.IN_POINTER_TX_ONLY]
and access_list_to == AccessListTo.POINTER_ADDRESS
)
else gas_costs.G_COLD_SLOAD
)
# contract address access
+ (
gas_costs.G_WARM_ACCOUNT_ACCESS
if (
access_list_rule in [AccessListCall.IN_BOTH_TX, AccessListCall.IN_POINTER_TX_ONLY]
and access_list_to == AccessListTo.CONTRACT_ADDRESS
)
else gas_costs.G_COLD_ACCOUNT_ACCESS
)
+ G_CALL_OPCODE
+ opcodes_price
)

contract = pre.deploy_contract(code=Op.SSTORE(call_worked, Op.ADD(Op.SLOAD(call_worked), 1)))

# Op.CALLDATASIZE() does not work with kwargs
storage_normal: Storage = Storage()
contract_test_normal = pre.deploy_contract(
code=Op.MSTORE(1000, Op.GAS())
+ Op.CALL(100_000, contract, 0, 0, Op.CALLDATASIZE(), 0, 0)
+ Op.CALL(gas=100_000, address=contract)
+ Op.SSTORE(
storage_normal.store_next(
(
20341
if access_list_rule == AccessListCall.BOTH
or access_list_rule == AccessListCall.NORMAL_ONLY
else 24841 # 20000 sstore, 100 sload
),
"normal_call_price",
),
storage_normal.store_next(direct_call_gas, "normal_call_price"),
Op.SUB(Op.MLOAD(1000), Op.GAS()),
)
)

storage_pointer: Storage = Storage()
contract_test_pointer = pre.deploy_contract(
code=Op.MSTORE(1000, Op.GAS())
+ Op.CALL(100_000, pointer_a, 0, 0, Op.CALLDATASIZE(), 0, 0)
+ Op.CALL(gas=100_000, address=pointer_a)
+ Op.SSTORE(
storage_pointer.store_next(
(
22941
if access_list_rule == AccessListCall.BOTH
or access_list_rule == AccessListCall.POINTER_ONLY
else (
24941 # setting pointer once again in each transaction reduces the price
if pointer_definition == PointerDefinition.IN_TX2_ONLY
or pointer_definition == PointerDefinition.IN_BOTH_TX
else 27441
)
),
"pointer_call_price",
),
storage_pointer.store_next(pointer_call_gas, "pointer_call_price"),
Op.SUB(Op.MLOAD(1000), Op.GAS()),
)
)
Expand Down Expand Up @@ -634,7 +694,7 @@ def test_gas_diff_pointer_vs_direct_call(
)
]
if pointer_definition == PointerDefinition.IN_BOTH_TX
or pointer_definition == PointerDefinition.IN_TX1_ONLY
or pointer_definition == PointerDefinition.IN_NORMAL_TX_ONLY
else None
),
access_list=(
Expand All @@ -644,8 +704,8 @@ def test_gas_diff_pointer_vs_direct_call(
storage_keys=[call_worked],
)
]
if access_list_rule == AccessListCall.BOTH
or access_list_rule == AccessListCall.NORMAL_ONLY
if access_list_rule == AccessListCall.IN_BOTH_TX
or access_list_rule == AccessListCall.IN_NORMAL_TX_ONLY
else None
),
)
Expand All @@ -664,18 +724,20 @@ def test_gas_diff_pointer_vs_direct_call(
)
]
if pointer_definition == PointerDefinition.IN_BOTH_TX
or pointer_definition == PointerDefinition.IN_TX2_ONLY
or pointer_definition == PointerDefinition.IN_POINTER_TX_ONLY
else None
),
access_list=(
[
AccessList(
address=pointer_a,
address=(
pointer_a if access_list_to == AccessListTo.POINTER_ADDRESS else contract
),
storage_keys=[call_worked],
)
]
if access_list_rule == AccessListCall.BOTH
or access_list_rule == AccessListCall.POINTER_ONLY
if access_list_rule == AccessListCall.IN_BOTH_TX
or access_list_rule == AccessListCall.IN_POINTER_TX_ONLY
else None
),
)
Expand Down

0 comments on commit b01c835

Please sign in to comment.