Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions packages/testing/src/execution_testing/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
BlockchainTest,
BlockchainTestFiller,
Header,
OpcodeTarget,
StateTest,
StateTestFiller,
TransactionTest,
Expand Down Expand Up @@ -173,6 +174,7 @@
"Macros",
"MemoryVariable",
"NetworkWrappedTransaction",
"OpcodeTarget",
"Op",
"Opcode",
"OpcodeCallArg",
Expand Down
8 changes: 7 additions & 1 deletion packages/testing/src/execution_testing/specs/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@

from .base import BaseTest, TestSpec
from .base_static import BaseStaticTest
from .benchmark import BenchmarkTest, BenchmarkTestFiller, BenchmarkTestSpec
from .benchmark import (
BenchmarkTest,
BenchmarkTestFiller,
BenchmarkTestSpec,
OpcodeTarget,
)
from .blobs import BlobsTest, BlobsTestFiller, BlobsTestSpec
from .blockchain import (
Block,
Expand Down Expand Up @@ -35,6 +40,7 @@
"BlockchainTestSpec",
"Block",
"Header",
"OpcodeTarget",
"StateStaticTest",
"StateTest",
"StateTestFiller",
Expand Down
27 changes: 25 additions & 2 deletions packages/testing/src/execution_testing/specs/benchmark.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,24 @@
from .blockchain import Block, BlockchainTest


@dataclass(frozen=True)
class OpcodeTarget:
"""
Map a display name to an underlying opcode for count validation.

Use when the fixture metadata should show a descriptive label (e.g. a
precompile name) while opcode-count validation targets the real EVM
opcode that gets executed (e.g. STATICCALL).
"""

name: str
opcode: Op

def __str__(self) -> str:
"""Return the display name."""
return self.name


@dataclass(kw_only=True)
class BenchmarkCodeGenerator(ABC):
"""Abstract base class for generating benchmark bytecode."""
Expand Down Expand Up @@ -287,7 +305,7 @@ class BenchmarkTest(BaseTest):
default_factory=lambda: int(Environment().gas_limit)
)
fixed_opcode_count: float | None = None
target_opcode: Op | None = None
target_opcode: Op | OpcodeTarget | None = None
code_generator: BenchmarkCodeGenerator | None = None
# By default, benchmark tests require neither of these
include_full_post_state_in_output: bool = False
Expand Down Expand Up @@ -523,7 +541,12 @@ def _verify_target_opcode_count(
# fixed_opcode_count is in thousands units
expected = self.fixed_opcode_count * 1000

actual = opcode_count.root.get(self.target_opcode, 0)
count_opcode = (
self.target_opcode.opcode
if isinstance(self.target_opcode, OpcodeTarget)
else self.target_opcode
)
actual = opcode_count.root.get(count_opcode, 0)
tolerance = expected * 0.05 # 5% tolerance

if abs(actual - expected) > tolerance:
Expand Down
25 changes: 25 additions & 0 deletions tests/benchmark/compute/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
Initcode,
IteratingBytecode,
Op,
OpcodeTarget,
TransactionWithCost,
While,
compute_create2_address,
Expand All @@ -27,6 +28,30 @@
FieldElement,
)


class Precompile:
"""Target opcode labels for precompile benchmarks."""

ECRECOVER = OpcodeTarget("ECRECOVER", Op.STATICCALL)
SHA256 = OpcodeTarget("SHA2-256", Op.STATICCALL)
RIPEMD160 = OpcodeTarget("RIPEMD-160", Op.STATICCALL)
IDENTITY = OpcodeTarget("IDENTITY", Op.STATICCALL)
MODEXP = OpcodeTarget("MODEXP", Op.STATICCALL)
BN128_ADD = OpcodeTarget("BN128_ADD", Op.STATICCALL)
BN128_MUL = OpcodeTarget("BN128_MUL", Op.STATICCALL)
BN128_PAIRING = OpcodeTarget("BN128_PAIRING", Op.STATICCALL)
BLAKE2F = OpcodeTarget("BLAKE2F", Op.STATICCALL)
POINT_EVALUATION = OpcodeTarget("POINT_EVALUATION", Op.STATICCALL)
P256VERIFY = OpcodeTarget("P256VERIFY", Op.STATICCALL)
BLS12_G1ADD = OpcodeTarget("BLS12_G1ADD", Op.STATICCALL)
BLS12_G1MSM = OpcodeTarget("BLS12_G1MSM", Op.STATICCALL)
BLS12_G2ADD = OpcodeTarget("BLS12_G2ADD", Op.STATICCALL)
BLS12_G2MSM = OpcodeTarget("BLS12_G2MSM", Op.STATICCALL)
BLS12_PAIRING = OpcodeTarget("BLS12_PAIRING", Op.STATICCALL)
BLS12_MAP_FP_TO_G1 = OpcodeTarget("BLS12_MAP_FP_TO_G1", Op.STATICCALL)
BLS12_MAP_FP2_TO_G2 = OpcodeTarget("BLS12_MAP_FP2_TO_G2", Op.STATICCALL)


DEFAULT_BINOP_ARGS = (
0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F,
0x73EDA753299D7D483339D80809A1D80553BDA402FFFE5BFEFFFFFFFF00000001,
Expand Down
61 changes: 49 additions & 12 deletions tests/benchmark/compute/precompile/test_alt_bn128.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,17 @@
Fork,
JumpLoopGenerator,
Op,
OpcodeTarget,
Transaction,
While,
)
from py_ecc.bn128 import G1, G2, multiply

from ..helpers import concatenate_parameters
from ..helpers import Precompile, concatenate_parameters


@pytest.mark.parametrize(
"precompile_address,calldata",
"precompile_address,calldata,target",
[
pytest.param(
0x06,
Expand All @@ -34,6 +35,7 @@
"06614E20C147E940F2D70DA3F74C9A17DF361706A4485C742BD6788478FA17D7",
]
),
Precompile.BN128_ADD,
id="bn128_add",
marks=pytest.mark.repricing,
),
Expand All @@ -49,6 +51,7 @@
"0000000000000000000000000000000000000000000000000000000000000000",
]
),
Precompile.BN128_ADD,
id="bn128_add_infinities",
marks=pytest.mark.repricing,
),
Expand All @@ -64,6 +67,7 @@
"0000000000000000000000000000000000000000000000000000000000000002",
]
),
Precompile.BN128_ADD,
id="bn128_add_1_2",
),
pytest.param(
Expand All @@ -75,6 +79,7 @@
"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF",
]
),
Precompile.BN128_MUL,
id="bn128_mul",
),
# Ported from
Expand All @@ -88,6 +93,7 @@
"0000000000000000000000000000000000000000000000000000000000000002",
]
),
Precompile.BN128_MUL,
id="bn128_mul_infinities_2_scalar",
),
# Ported from
Expand All @@ -101,6 +107,7 @@
"25f8c89ea3437f44f8fc8b6bfbb6312074dc6f983809a5e809ff4e1d076dd585",
]
),
Precompile.BN128_MUL,
id="bn128_mul_infinities_32_byte_scalar",
marks=pytest.mark.repricing,
),
Expand All @@ -115,6 +122,7 @@
"0000000000000000000000000000000000000000000000000000000000000002",
]
),
Precompile.BN128_MUL,
id="bn128_mul_1_2_2_scalar",
),
# Ported from
Expand All @@ -128,6 +136,7 @@
"25f8c89ea3437f44f8fc8b6bfbb6312074dc6f983809a5e809ff4e1d076dd585",
]
),
Precompile.BN128_MUL,
id="bn128_mul_1_2_32_byte_scalar",
),
# Ported from
Expand All @@ -141,6 +150,7 @@
"0000000000000000000000000000000000000000000000000000000000000002",
]
),
Precompile.BN128_MUL,
id="bn128_mul_32_byte_coord_and_2_scalar",
marks=pytest.mark.repricing,
),
Expand All @@ -155,6 +165,7 @@
"25f8c89ea3437f44f8fc8b6bfbb6312074dc6f983809a5e809ff4e1d076dd585",
]
),
Precompile.BN128_MUL,
id="bn128_mul_32_byte_coord_and_scalar",
marks=pytest.mark.repricing,
),
Expand All @@ -178,6 +189,7 @@
"12C85EA5DB8C6DEB4AAB71808DCB408FE3D1E7690C43D37B4CE6CC0166FA7DAA",
]
),
Precompile.BN128_PAIRING,
id="bn128_two_pairings",
),
pytest.param(
Expand All @@ -193,11 +205,17 @@
"120A2A4CF30C1BF9845F20C6FE39E07EA2CCE61F0C9BB048165FE5E4DE877550",
]
),
Precompile.BN128_PAIRING,
id="bn128_one_pairing",
),
# Ported from
# https://github.com/NethermindEth/nethermind/blob/ceb8d57b8530ce8181d7427c115ca593386909d6/tools/EngineRequestsGenerator/TestCase.cs#L353
pytest.param(0x08, [], id="ec_pairing_zero_input"),
pytest.param(
0x08,
[],
Precompile.BN128_PAIRING,
id="ec_pairing_zero_input",
),
pytest.param(
0x08,
concatenate_parameters(
Expand All @@ -218,11 +236,13 @@
"3a8eb0b0996252cb548a4487da97b02422ebc0e834613f954de6c7e0afdc1fc0",
]
),
Precompile.BN128_PAIRING,
id="ec_pairing_2_sets",
),
pytest.param(
0x08,
concatenate_parameters([""]),
Precompile.BN128_PAIRING,
id="ec_pairing_1_pair",
),
pytest.param(
Expand All @@ -245,6 +265,7 @@
"12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa",
]
),
Precompile.BN128_PAIRING,
id="ec_pairing_2_pair",
),
pytest.param(
Expand Down Expand Up @@ -272,6 +293,7 @@
"12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa",
]
),
Precompile.BN128_PAIRING,
id="ec_pairing_3_pair",
),
pytest.param(
Expand Down Expand Up @@ -308,6 +330,7 @@
"2dc4cb08068b4aa5f14b7f1096ab35d5c13d78319ec7e66e9f67a1ff20cbbf03",
]
),
Precompile.BN128_PAIRING,
id="ec_pairing_4_pair",
),
pytest.param(
Expand Down Expand Up @@ -351,6 +374,7 @@
"1ac5dac62d2332faa8069faca3b0d27fcdf95d8c8bafc9074ee72b5c1f33aa70",
]
),
Precompile.BN128_PAIRING,
id="ec_pairing_5_pair",
),
pytest.param(
Expand All @@ -360,6 +384,7 @@
"0000000000000000000000000000000000000000000000000000000000000000",
]
),
Precompile.BN128_PAIRING,
id="ec_pairing_1_pair_empty",
),
],
Expand All @@ -368,6 +393,7 @@ def test_alt_bn128(
benchmark_test: BenchmarkTestFiller,
precompile_address: Address,
calldata: bytes,
target: OpcodeTarget,
) -> None:
"""Benchmark ALT_BN128 precompile."""
attack_block = Op.POP(
Expand All @@ -377,7 +403,7 @@ def test_alt_bn128(
)

benchmark_test(
target_opcode=Op.STATICCALL,
target_opcode=target,
code_generator=JumpLoopGenerator(
setup=Op.CALLDATACOPY(0, 0, Op.CALLDATASIZE),
attack_block=attack_block,
Expand Down Expand Up @@ -493,7 +519,7 @@ def test_bn128_pairings_amortized(
)

benchmark_test(
target_opcode=Op.STATICCALL,
target_opcode=Precompile.BN128_PAIRING,
code_generator=JumpLoopGenerator(
setup=setup,
attack_block=attack_block,
Expand All @@ -520,7 +546,7 @@ def test_alt_bn128_benchmark(
)

benchmark_test(
target_opcode=Op.STATICCALL,
target_opcode=Precompile.BN128_PAIRING,
code_generator=JumpLoopGenerator(
setup=Op.CALLDATACOPY(0, 0, Op.CALLDATASIZE),
attack_block=attack_block,
Expand Down Expand Up @@ -633,7 +659,7 @@ def test_ec_pairing(
seed_offset += per_tx_variants

benchmark_test(
target_opcode=Op.STATICCALL,
target_opcode=Precompile.BN128_PAIRING,
skip_gas_used_validation=True,
blocks=[Block(txs=txs)],
)
Expand All @@ -652,11 +678,21 @@ def _generate_g1_point(seed: int) -> Bytes:

@pytest.mark.repricing
@pytest.mark.parametrize(
"precompile_address,scalar",
"precompile_address,scalar,target",
[
pytest.param(0x06, None, id="ec_add"),
pytest.param(0x07, 2, id="ec_mul_small_scalar"),
pytest.param(0x07, 2**256 - 1, id="ec_mul_max_scalar"),
pytest.param(0x06, None, Precompile.BN128_ADD, id="ec_add"),
pytest.param(
0x07,
2,
Precompile.BN128_MUL,
id="ec_mul_small_scalar",
),
pytest.param(
0x07,
2**256 - 1,
Precompile.BN128_MUL,
id="ec_mul_max_scalar",
),
],
)
def test_alt_bn128_uncachable(
Expand All @@ -667,6 +703,7 @@ def test_alt_bn128_uncachable(
tx_gas_limit: int,
precompile_address: Address,
scalar: int | None,
target: OpcodeTarget,
) -> None:
"""
Benchmark ecAdd/ecMul with unique input per call.
Expand Down Expand Up @@ -722,6 +759,6 @@ def test_alt_bn128_uncachable(
seed += 1

benchmark_test(
target_opcode=Op.STATICCALL,
target_opcode=target,
blocks=[Block(txs=txs)],
)
Loading
Loading