Skip to content

Commit f14624b

Browse files
jsignchfast
andauthored
zkevm: add remaining precompiles (#1581)
* zkevm: more precompiles Signed-off-by: Ignacio Hagopian <jsign.uy@gmail.com> * improve params format Signed-off-by: Ignacio Hagopian <jsign.uy@gmail.com> * improve parameters Signed-off-by: Ignacio Hagopian <jsign.uy@gmail.com> * add bls12_g1msm Signed-off-by: Ignacio Hagopian <jsign.uy@gmail.com> * add bls12_g2msm Signed-off-by: Ignacio Hagopian <jsign.uy@gmail.com> * add blas12 pairing check Signed-off-by: Ignacio Hagopian <jsign.uy@gmail.com> * add bls_map tests Signed-off-by: Ignacio Hagopian <jsign.uy@gmail.com> * rebase & format Signed-off-by: Ignacio Hagopian <jsign.uy@gmail.com> * remove input length parameter Signed-off-by: Ignacio Hagopian <jsign.uy@gmail.com> * use bls12381 precompile addresses from spec and simplify parameter Signed-off-by: Ignacio Hagopian <jsign.uy@gmail.com> * make the linter happy Signed-off-by: Ignacio Hagopian <jsign.uy@gmail.com> * use symbolic parameters for bls12381 precompiles Signed-off-by: Ignacio Hagopian <jsign.uy@gmail.com> * lints Signed-off-by: Ignacio Hagopian <jsign.uy@gmail.com> * linter Signed-off-by: Ignacio Hagopian <jsign.uy@gmail.com> * add slow marker Signed-off-by: Ignacio Hagopian <jsign.uy@gmail.com> * rebase fixes Signed-off-by: Ignacio Hagopian <jsign.uy@gmail.com> * reuse some constants and precompile inputs * comments and fixes Signed-off-by: Ignacio Hagopian <jsign.uy@gmail.com> * clarify comment Signed-off-by: Ignacio Hagopian <jsign.uy@gmail.com> --------- Signed-off-by: Ignacio Hagopian <jsign.uy@gmail.com> Co-authored-by: Paweł Bylica <pawel@ethereum.org>
1 parent b5f1d95 commit f14624b

File tree

1 file changed

+176
-14
lines changed

1 file changed

+176
-14
lines changed

tests/zkevm/test_worst_compute.py

Lines changed: 176 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
import math
99
import random
10+
from typing import cast
1011

1112
import pytest
1213

@@ -19,17 +20,21 @@
1920
Bytecode,
2021
Environment,
2122
Transaction,
22-
While,
2323
)
24+
from ethereum_test_tools.code.generators import While
2425
from ethereum_test_tools.vm.opcode import Opcodes as Op
25-
from ethereum_test_vm import Opcode
26+
from ethereum_test_vm.opcode import Opcode
27+
from tests.cancun.eip4844_blobs.spec import Spec as BlobsSpec
28+
from tests.istanbul.eip152_blake2.common import Blake2bInput
29+
from tests.istanbul.eip152_blake2.spec import Spec as Blake2bSpec
30+
from tests.prague.eip2537_bls_12_381_precompiles import spec as bls12381_spec
31+
from tests.prague.eip2537_bls_12_381_precompiles.spec import BytesConcatenation
2632

2733
REFERENCE_SPEC_GIT_PATH = "TODO"
2834
REFERENCE_SPEC_VERSION = "TODO"
2935

3036
MAX_CODE_SIZE = 24 * 1024
3137
KECCAK_RATE = 136
32-
ECRECOVER_GAS_COST = 3_000
3338

3439

3540
@pytest.mark.valid_from("Cancun")
@@ -116,6 +121,7 @@ def test_worst_keccak(
116121
pytest.param(0x04, 15, 3, 1, id="IDENTITY"),
117122
],
118123
)
124+
@pytest.mark.slow()
119125
def test_worst_precompile_only_data_input(
120126
blockchain_test: BlockchainTestFiller,
121127
pre: Alloc,
@@ -237,23 +243,179 @@ def test_worst_modexp(
237243

238244

239245
@pytest.mark.valid_from("Cancun")
240-
def test_worst_ecrecover(
246+
@pytest.mark.parametrize(
247+
"precompile_address,parameters",
248+
[
249+
pytest.param(
250+
0x01,
251+
[
252+
# The inputs below are a valid signature, thus ECRECOVER call won't
253+
# be short-circuited by validations and do actual work.
254+
"38D18ACB67D25C8BB9942764B62F18E17054F66A817BD4295423ADF9ED98873E",
255+
"000000000000000000000000000000000000000000000000000000000000001B",
256+
"38D18ACB67D25C8BB9942764B62F18E17054F66A817BD4295423ADF9ED98873E",
257+
"789D1DD423D25F0772D2748D60F7E4B81BB14D086EBA8E8E8EFB6DCFF8A4AE02",
258+
],
259+
id="ecrecover",
260+
),
261+
pytest.param(
262+
0x06,
263+
[
264+
"18B18ACFB4C2C30276DB5411368E7185B311DD124691610C5D3B74034E093DC9",
265+
"063C909C4720840CB5134CB9F59FA749755796819658D32EFC0D288198F37266",
266+
"07C2B7F58A84BD6145F00C9C2BC0BB1A187F20FF2C92963A88019E7C6A014EED",
267+
"06614E20C147E940F2D70DA3F74C9A17DF361706A4485C742BD6788478FA17D7",
268+
],
269+
id="bn128_add",
270+
),
271+
pytest.param(
272+
0x07,
273+
[
274+
"1A87B0584CE92F4593D161480614F2989035225609F08058CCFA3D0F940FEBE3",
275+
"1A2F3C951F6DADCC7EE9007DFF81504B0FCD6D7CF59996EFDC33D92BF7F9F8F6",
276+
"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF",
277+
],
278+
id="bn128_mul",
279+
),
280+
pytest.param(
281+
0x08,
282+
[
283+
# TODO: the following are only two inputs, but this can be extended
284+
# to more inputs to amortize costs as much as possible. Additionally,
285+
# there might be worse pairings that can be used.
286+
#
287+
# First pairing
288+
"1C76476F4DEF4BB94541D57EBBA1193381FFA7AA76ADA664DD31C16024C43F59",
289+
"3034DD2920F673E204FEE2811C678745FC819B55D3E9D294E45C9B03A76AEF41",
290+
"209DD15EBFF5D46C4BD888E51A93CF99A7329636C63514396B4A452003A35BF7",
291+
"04BF11CA01483BFA8B34B43561848D28905960114C8AC04049AF4B6315A41678",
292+
"2BB8324AF6CFC93537A2AD1A445CFD0CA2A71ACD7AC41FADBF933C2A51BE344D",
293+
"120A2A4CF30C1BF9845F20C6FE39E07EA2CCE61F0C9BB048165FE5E4DE877550",
294+
# Second pairing
295+
"111E129F1CF1097710D41C4AC70FCDFA5BA2023C6FF1CBEAC322DE49D1B6DF7C",
296+
"103188585E2364128FE25C70558F1560F4F9350BAF3959E603CC91486E110936",
297+
"198E9393920D483A7260BFB731FB5D25F1AA493335A9E71297E485B7AEF312C2",
298+
"1800DEEF121F1E76426A00665E5C4479674322D4F75EDADD46DEBD5CD992F6ED",
299+
"090689D0585FF075EC9E99AD690C3395BC4B313370B38EF355ACDADCD122975B",
300+
"12C85EA5DB8C6DEB4AAB71808DCB408FE3D1E7690C43D37B4CE6CC0166FA7DAA",
301+
],
302+
id="bn128_pairing",
303+
),
304+
pytest.param(
305+
Blake2bSpec.BLAKE2_PRECOMPILE_ADDRESS,
306+
[
307+
Blake2bInput(rounds=0xFFFF, f=True).create_blake2b_tx_data(),
308+
],
309+
id="blake2f",
310+
),
311+
pytest.param(
312+
BlobsSpec.POINT_EVALUATION_PRECOMPILE_ADDRESS,
313+
[
314+
"01E798154708FE7789429634053CBF9F99B619F9F084048927333FCE637F549B",
315+
"564C0A11A0F704F4FC3E8ACFE0F8245F0AD1347B378FBF96E206DA11A5D36306",
316+
"24D25032E67A7E6A4910DF5834B8FE70E6BCFEEAC0352434196BDF4B2485D5A1",
317+
"8F59A8D2A1A625A17F3FEA0FE5EB8C896DB3764F3185481BC22F91B4AAFFCCA25F26936857BC3A7C2539EA8EC3A952B7",
318+
"873033E038326E87ED3E1276FD140253FA08E9FC25FB2D9A98527FC22A2C9612FBEAFDAD446CBC7BCDBDCD780AF2C16A",
319+
],
320+
id="point_evaluation",
321+
),
322+
pytest.param(
323+
bls12381_spec.Spec.G1ADD,
324+
[
325+
bls12381_spec.Spec.G1,
326+
bls12381_spec.Spec.P1,
327+
],
328+
id="bls12_g1add",
329+
),
330+
pytest.param(
331+
bls12381_spec.Spec.G1MSM,
332+
[
333+
(bls12381_spec.Spec.P1 + bls12381_spec.Scalar(bls12381_spec.Spec.Q))
334+
* (len(bls12381_spec.Spec.G1MSM_DISCOUNT_TABLE) - 1),
335+
],
336+
id="bls12_g1msm",
337+
),
338+
pytest.param(
339+
bls12381_spec.Spec.G2ADD,
340+
[
341+
bls12381_spec.Spec.G2,
342+
bls12381_spec.Spec.P2,
343+
],
344+
id="bls12_g2add",
345+
),
346+
pytest.param(
347+
bls12381_spec.Spec.G2MSM,
348+
[
349+
# TODO: the //2 is required due to a limitation of the max contract size limit.
350+
# In a further iteration we can insert the inputs as calldata or storage and avoid
351+
# having to do PUSHes which has this limtiation. This also applies to G1MSM.
352+
(bls12381_spec.Spec.P2 + bls12381_spec.Scalar(bls12381_spec.Spec.Q))
353+
* (len(bls12381_spec.Spec.G2MSM_DISCOUNT_TABLE) // 2),
354+
],
355+
id="bls12_g2msm",
356+
),
357+
pytest.param(
358+
bls12381_spec.Spec.PAIRING,
359+
[
360+
bls12381_spec.Spec.G1,
361+
bls12381_spec.Spec.G2,
362+
],
363+
id="bls12_pairing_check",
364+
),
365+
pytest.param(
366+
bls12381_spec.Spec.MAP_FP_TO_G1,
367+
[
368+
bls12381_spec.FP(bls12381_spec.Spec.P - 1),
369+
],
370+
id="bls12_fp_to_g1",
371+
),
372+
pytest.param(
373+
bls12381_spec.Spec.MAP_FP2_TO_G2,
374+
[
375+
bls12381_spec.FP2((bls12381_spec.Spec.P - 1, bls12381_spec.Spec.P - 1)),
376+
],
377+
id="bls12_fp_to_g2",
378+
),
379+
],
380+
)
381+
@pytest.mark.slow()
382+
def test_worst_precompile_fixed_cost(
241383
blockchain_test: BlockchainTestFiller,
242384
pre: Alloc,
243-
fork: Fork,
385+
precompile_address: Address,
386+
parameters: list[str] | list[BytesConcatenation] | list[bytes],
244387
):
245-
"""Test running a block with as many ECRECOVER calls as possible."""
388+
"""Test running a block filled with a precompile with fixed cost."""
246389
env = Environment()
247390

248-
# Calldata
249-
calldata = (
250-
Op.MSTORE(0 * 32, 0x38D18ACB67D25C8BB9942764B62F18E17054F66A817BD4295423ADF9ED98873E)
251-
+ Op.MSTORE(1 * 32, 27)
252-
+ Op.MSTORE(2 * 32, 0x38D18ACB67D25C8BB9942764B62F18E17054F66A817BD4295423ADF9ED98873E)
253-
+ Op.MSTORE(3 * 32, 0x789D1DD423D25F0772D2748D60F7E4B81BB14D086EBA8E8E8EFB6DCFF8A4AE02)
254-
)
391+
concatenated_bytes: bytes
392+
if all(isinstance(p, str) for p in parameters):
393+
parameters_str = cast(list[str], parameters)
394+
concatenated_hex_string = "".join(parameters_str)
395+
concatenated_bytes = bytes.fromhex(concatenated_hex_string)
396+
elif all(isinstance(p, (bytes, BytesConcatenation)) for p in parameters):
397+
parameters_bytes_list = [
398+
bytes(p) for p in cast(list[BytesConcatenation | bytes], parameters)
399+
]
400+
concatenated_bytes = b"".join(parameters_bytes_list)
401+
else:
402+
raise TypeError(
403+
"parameters must be a list of strings (hex) "
404+
"or a list of byte-like objects (bytes or BytesConcatenation)."
405+
)
255406

256-
attack_block = Op.POP(Op.STATICCALL(ECRECOVER_GAS_COST, 0x1, 0, 32 * 4, 0, 0))
407+
padding_length = (32 - (len(concatenated_bytes) % 32)) % 32
408+
input_bytes = concatenated_bytes + b"\x00" * padding_length
409+
410+
calldata = Bytecode()
411+
for i in range(0, len(input_bytes), 32):
412+
chunk = input_bytes[i : i + 32]
413+
value_to_store = int.from_bytes(chunk, "big")
414+
calldata += Op.MSTORE(i, value_to_store)
415+
416+
attack_block = Op.POP(
417+
Op.STATICCALL(Op.GAS, precompile_address, 0, len(concatenated_bytes), 0, 0)
418+
)
257419
code = code_loop_precompile_call(calldata, attack_block)
258420
code_address = pre.deploy_contract(code=bytes(code))
259421

0 commit comments

Comments
 (0)