@pytest.mark.repricing
@pytest.mark.parametrize(
"precompile_address,generate_point,target",
[
pytest.param(
bls12381_spec.Spec.G1MSM,
_generate_bls12_g1_point,
Precompile.BLS12_G1MSM,
id="bls12_g1msm",
),
pytest.param(
bls12381_spec.Spec.G2MSM,
_generate_bls12_g2_point,
Precompile.BLS12_G2MSM,
id="bls12_g2msm",
),
],
)
@pytest.mark.parametrize(
"k",
[
1,
16,
64,
# A G2 MSM at k=128 costs 1.5M gas.
pytest.param(128, marks=pytest.mark.slow),
],
)
def test_bls12_msm(
benchmark_test: BenchmarkTestFiller,
pre: Alloc,
fork: Fork,
gas_benchmark_value: int,
tx_gas_limit: int,
precompile_address: int,
generate_point: Callable[[int], Bytes],
target: OpcodeTarget,
k: int,
) -> None:
"""Benchmark a BLS12 MSM precompile with varying number of points."""
if precompile_address not in fork.precompiles():
pytest.skip(f"{target} precompile not enabled")
input_data = _msm_worstcase_input(k, generate_point)
gas_calc_map = build_gas_calculation_function_map(fork.gas_costs())
precompile_cost = gas_calc_map[precompile_address](len(input_data))
# Each transaction of the split gets at most this much gas; if one
# precompile call does not fit, the loop can never complete an iteration.
if precompile_cost > min(tx_gas_limit, gas_benchmark_value):
pytest.skip(
f"one {len(input_data)}-byte MSM call costs {precompile_cost} "
"gas, more than a single transaction can spend"
)
benchmark_test(
target_opcode=target,
code_generator=JumpLoopGenerator(
setup=_load_input_from_code(pre, fork, input_data),
attack_block=Op.POP(
Op.STATICCALL(
gas=Op.GAS,
address=precompile_address,
args_size=len(input_data),
),
),
),
)