|
7 | 7 |
|
8 | 8 | import math
|
9 | 9 | import random
|
| 10 | +from typing import cast |
10 | 11 |
|
11 | 12 | import pytest
|
12 | 13 |
|
|
19 | 20 | Bytecode,
|
20 | 21 | Environment,
|
21 | 22 | Transaction,
|
22 |
| - While, |
23 | 23 | )
|
| 24 | +from ethereum_test_tools.code.generators import While |
24 | 25 | 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 |
26 | 32 |
|
27 | 33 | REFERENCE_SPEC_GIT_PATH = "TODO"
|
28 | 34 | REFERENCE_SPEC_VERSION = "TODO"
|
29 | 35 |
|
30 | 36 | MAX_CODE_SIZE = 24 * 1024
|
31 | 37 | KECCAK_RATE = 136
|
32 |
| -ECRECOVER_GAS_COST = 3_000 |
33 | 38 |
|
34 | 39 |
|
35 | 40 | @pytest.mark.valid_from("Cancun")
|
@@ -116,6 +121,7 @@ def test_worst_keccak(
|
116 | 121 | pytest.param(0x04, 15, 3, 1, id="IDENTITY"),
|
117 | 122 | ],
|
118 | 123 | )
|
| 124 | +@pytest.mark.slow() |
119 | 125 | def test_worst_precompile_only_data_input(
|
120 | 126 | blockchain_test: BlockchainTestFiller,
|
121 | 127 | pre: Alloc,
|
@@ -237,23 +243,179 @@ def test_worst_modexp(
|
237 | 243 |
|
238 | 244 |
|
239 | 245 | @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( |
241 | 383 | blockchain_test: BlockchainTestFiller,
|
242 | 384 | pre: Alloc,
|
243 |
| - fork: Fork, |
| 385 | + precompile_address: Address, |
| 386 | + parameters: list[str] | list[BytesConcatenation] | list[bytes], |
244 | 387 | ):
|
245 |
| - """Test running a block with as many ECRECOVER calls as possible.""" |
| 388 | + """Test running a block filled with a precompile with fixed cost.""" |
246 | 389 | env = Environment()
|
247 | 390 |
|
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 | + ) |
255 | 406 |
|
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 | + ) |
257 | 419 | code = code_loop_precompile_call(calldata, attack_block)
|
258 | 420 | code_address = pre.deploy_contract(code=bytes(code))
|
259 | 421 |
|
|
0 commit comments