Skip to content

Commit c91549c

Browse files
jsignmarioevz
andauthored
new(tests/zkEVM): add modexp attack (#1523)
* add modexp tests Signed-off-by: Ignacio Hagopian <[email protected]> * leave only 36M gas limit Signed-off-by: Ignacio Hagopian <[email protected]> * adjustments Signed-off-by: Ignacio Hagopian <[email protected]> * lints Signed-off-by: Ignacio Hagopian <[email protected]> * lints and cleanup Signed-off-by: Ignacio Hagopian <[email protected]> * lints Signed-off-by: Ignacio Hagopian <[email protected]> * Update tests/zkevm/test_worst_compute.py Co-authored-by: Mario Vega <[email protected]> * Update tests/zkevm/test_worst_compute.py Co-authored-by: Mario Vega <[email protected]> * Update tests/zkevm/test_worst_compute.py Co-authored-by: Mario Vega <[email protected]> * feedback Signed-off-by: Ignacio Hagopian <[email protected]> --------- Signed-off-by: Ignacio Hagopian <[email protected]> Co-authored-by: Mario Vega <[email protected]>
1 parent 283ca18 commit c91549c

File tree

1 file changed

+67
-0
lines changed

1 file changed

+67
-0
lines changed

tests/zkevm/test_worst_compute.py

+67
Original file line numberDiff line numberDiff line change
@@ -103,3 +103,70 @@ def test_worst_keccak(
103103
post={},
104104
blocks=[Block(txs=[tx])],
105105
)
106+
107+
108+
@pytest.mark.zkevm
109+
@pytest.mark.valid_from("Cancun")
110+
@pytest.mark.parametrize(
111+
"gas_limit",
112+
[
113+
Environment().gas_limit,
114+
],
115+
)
116+
def test_worst_modexp(
117+
blockchain_test: BlockchainTestFiller,
118+
pre: Alloc,
119+
fork: Fork,
120+
gas_limit: int,
121+
):
122+
"""Test running a block with as many MODEXP calls as possible."""
123+
env = Environment(gas_limit=gas_limit)
124+
125+
base_mod_length = 32
126+
exp_length = 32
127+
128+
base = 2 ** (8 * base_mod_length) - 1
129+
mod = 2 ** (8 * base_mod_length) - 2 # Prevents base == mod
130+
exp = 2 ** (8 * exp_length) - 1
131+
132+
# MODEXP calldata
133+
calldata = (
134+
Op.MSTORE(0 * 32, base_mod_length)
135+
+ Op.MSTORE(1 * 32, exp_length)
136+
+ Op.MSTORE(2 * 32, base_mod_length)
137+
+ Op.MSTORE(3 * 32, base)
138+
+ Op.MSTORE(4 * 32, exp)
139+
+ Op.MSTORE(5 * 32, mod)
140+
)
141+
142+
# EIP-2565
143+
mul_complexity = math.ceil(base_mod_length / 8) ** 2
144+
iter_complexity = exp.bit_length() - 1
145+
gas_cost = math.floor((mul_complexity * iter_complexity) / 3)
146+
attack_block = Op.POP(Op.STATICCALL(gas_cost, 0x5, 0, 32 * 6, 0, 0))
147+
148+
# The attack contract is: JUMPDEST + [attack_block]* + PUSH0 + JUMP
149+
jumpdest = Op.JUMPDEST
150+
jump_back = Op.JUMP(len(calldata))
151+
max_iters_loop = (MAX_CODE_SIZE - len(calldata) - len(jumpdest) - len(jump_back)) // len(
152+
attack_block
153+
)
154+
code = calldata + jumpdest + sum([attack_block] * max_iters_loop) + jump_back
155+
if len(code) > MAX_CODE_SIZE:
156+
# Must never happen, but keep it as a sanity check.
157+
raise ValueError(f"Code size {len(code)} exceeds maximum code size {MAX_CODE_SIZE}")
158+
159+
code_address = pre.deploy_contract(code=code)
160+
161+
tx = Transaction(
162+
to=code_address,
163+
gas_limit=gas_limit,
164+
sender=pre.fund_eoa(),
165+
)
166+
167+
blockchain_test(
168+
env=env,
169+
pre=pre,
170+
post={},
171+
blocks=[Block(txs=[tx])],
172+
)

0 commit comments

Comments
 (0)