Skip to content

test_bn128_pairings_amortized()

Documentation for tests/benchmark/compute/precompile/test_alt_bn128.py::test_bn128_pairings_amortized@ffe48572.

Generate fixtures for these test cases for Bogota with:

fill -v tests/benchmark/compute/precompile/test_alt_bn128.py::test_bn128_pairings_amortized --gas-benchmark-values 1

Test running a block with as many BN128 pairings as possible.

Source code in tests/benchmark/compute/precompile/test_alt_bn128.py
def test_bn128_pairings_amortized(
    benchmark_test: BenchmarkTestFiller,
    fork: Fork,
    tx_gas_limit: int,
) -> None:
    """Test running a block with as many BN128 pairings as possible."""
    size_per_pairing = 192

    gsc = fork.gas_costs()
    base_cost = gsc.PRECOMPILE_ECPAIRING_BASE
    pairing_cost = gsc.PRECOMPILE_ECPAIRING_PER_POINT
    intrinsic_gas_calculator = fork.transaction_intrinsic_cost_calculator()
    mem_exp_gas_calculator = fork.memory_expansion_gas_calculator()
    warm_account_access_cost = Op.STATICCALL(
        gas=Op.GAS,
        address=Op.PUSH20(0),
        args_offset=Op.PUSH0,
        args_size=Op.PUSH0,
        ret_offset=Op.PUSH0,
        ret_size=Op.PUSH0,
        # gas accounting
        address_warm=True,
    ).gas_cost(fork)

    # This is a theoretical maximum number of pairings that can be done in a
    # block. It is only used for an upper bound for calculating the optimal
    # number of pairings below.
    maximum_number_of_pairings = (tx_gas_limit - base_cost) // pairing_cost

    # Discover the optimal number of pairings balancing two dimensions:
    # 1. Amortize the precompile base cost as much as possible.
    # 2. The cost of the memory expansion.
    max_pairings = 0
    optimal_per_call_num_pairings = 0
    for i in range(1, maximum_number_of_pairings + 1):
        # We'll pass all pairing arguments via calldata.
        available_gas_after_intrinsic = (
            tx_gas_limit
            - intrinsic_gas_calculator(
                calldata=[0xFF]
                * size_per_pairing
                * i  # 0xFF is to indicate non-
                # zero bytes.
            )
        )
        available_gas_after_expansion = max(
            0,
            available_gas_after_intrinsic
            - mem_exp_gas_calculator(new_bytes=i * size_per_pairing),
        )

        approx_gas_cost_per_call = (
            warm_account_access_cost + base_cost + i * pairing_cost
        )

        num_precompile_calls = (
            available_gas_after_expansion // approx_gas_cost_per_call
        )
        num_pairings_done = num_precompile_calls * i  # Each precompile call
        # does i pairings.

        if num_pairings_done > max_pairings:
            max_pairings = num_pairings_done
            optimal_per_call_num_pairings = i

    setup = Op.CALLDATACOPY(size=Op.CALLDATASIZE)
    attack_block = Op.POP(
        Op.STATICCALL(Op.GAS, EIP197Spec.ECPAIRING, 0, Op.CALLDATASIZE, 0, 0)
    )

    benchmark_test(
        target_opcode=Precompile.BN128_PAIRING,
        code_generator=JumpLoopGenerator(
            setup=setup,
            attack_block=attack_block,
            tx_kwargs={
                "data": _generate_bn128_pairs(
                    optimal_per_call_num_pairings, 42
                )
            },
        ),
    )

Parametrized Test Cases

This test generates 1 parametrized test case across 4 forks.