Skip to content

Commit 024b8a9

Browse files
committed
generic precompiles with only data parameter
Signed-off-by: Ignacio Hagopian <jsign.uy@gmail.com>
1 parent 9c9a63f commit 024b8a9

File tree

1 file changed

+89
-0
lines changed

1 file changed

+89
-0
lines changed

tests/zkevm/test_worst_compute.py

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
from ethereum_test_forks import Fork
1313
from ethereum_test_tools import (
14+
Address,
1415
Alloc,
1516
Block,
1617
BlockchainTestFiller,
@@ -113,6 +114,94 @@ def test_worst_keccak(
113114
)
114115

115116

117+
@pytest.mark.zkevm
118+
@pytest.mark.valid_from("Cancun")
119+
@pytest.mark.parametrize(
120+
"gas_limit",
121+
[
122+
Environment().gas_limit,
123+
],
124+
)
125+
@pytest.mark.parametrize(
126+
"address,static_cost,per_word_dynamic_cost,bytes_per_unit_of_work",
127+
[
128+
pytest.param(0x02, 60, 12, 64, id="SHA2-256"),
129+
pytest.param(0x03, 600, 120, 64, id="RIPEMD-160"),
130+
pytest.param(0x04, 15, 3, 1, id="IDENTITY"),
131+
],
132+
)
133+
def test_worst_precompile_only_data_input(
134+
blockchain_test: BlockchainTestFiller,
135+
pre: Alloc,
136+
fork: Fork,
137+
gas_limit: int,
138+
address: Address,
139+
static_cost: int,
140+
per_word_dynamic_cost: int,
141+
bytes_per_unit_of_work: int,
142+
):
143+
"""Test running a block with as many precompile calls which have a single `data` input."""
144+
env = Environment(gas_limit=gas_limit)
145+
146+
# Intrinsic gas cost is paid once.
147+
intrinsic_gas_calculator = fork.transaction_intrinsic_cost_calculator()
148+
available_gas = gas_limit - intrinsic_gas_calculator()
149+
150+
gsc = fork.gas_costs()
151+
mem_exp_gas_calculator = fork.memory_expansion_gas_calculator()
152+
153+
# Discover the optimal input size to maximize precompile calls, not precompile permutations.
154+
max_work = 0
155+
optimal_input_length = 0
156+
for input_length in range(1, 1_000_000, 32):
157+
staticcall_parameters_gas = (
158+
gsc.G_BASE # PUSH0 = arg offset
159+
+ gsc.G_BASE # PUSH0 = arg size
160+
+ gsc.G_BASE # PUSH0 = arg size
161+
+ gsc.G_VERY_LOW # PUSH0 = arg offset
162+
+ gsc.G_VERY_LOW # PUSHN = address
163+
+ gsc.G_BASE # GAS
164+
)
165+
iteration_gas_cost = (
166+
staticcall_parameters_gas
167+
+ +static_cost # Precompile static cost
168+
+ math.ceil(input_length / 32) * per_word_dynamic_cost # Precompile dynamic cost
169+
+ gsc.G_BASE # POP
170+
)
171+
# From the available gas, we substract the mem expansion costs considering we know the
172+
# current input size length.
173+
available_gas_after_expansion = max(
174+
0, available_gas - mem_exp_gas_calculator(new_bytes=input_length)
175+
)
176+
# Calculate how many calls we can do.
177+
num_calls = available_gas_after_expansion // iteration_gas_cost
178+
total_work = num_calls * math.ceil(input_length / bytes_per_unit_of_work)
179+
180+
# If we found an input size that is better (reg permutations/gas), then save it.
181+
if total_work > max_work:
182+
max_work = total_work
183+
optimal_input_length = input_length
184+
185+
calldata = Op.CODECOPY(0, 0, optimal_input_length)
186+
attack_block = Op.POP(Op.STATICCALL(Op.GAS, address, 0, optimal_input_length, 0, 0))
187+
code = code_loop_precompile_call(calldata, attack_block)
188+
189+
code_address = pre.deploy_contract(code=code)
190+
191+
tx = Transaction(
192+
to=code_address,
193+
gas_limit=gas_limit,
194+
sender=pre.fund_eoa(),
195+
)
196+
197+
blockchain_test(
198+
env=env,
199+
pre=pre,
200+
post={},
201+
blocks=[Block(txs=[tx])],
202+
)
203+
204+
116205
@pytest.mark.zkevm
117206
@pytest.mark.valid_from("Cancun")
118207
@pytest.mark.parametrize(

0 commit comments

Comments
 (0)