Skip to content

test_amortized_bn128_pairings()

Documentation for tests/benchmark/test_worst_compute.py::test_amortized_bn128_pairings@e9958ed2.

Generate fixtures for these test cases for Osaka with:

fill -v tests/benchmark/test_worst_compute.py::test_amortized_bn128_pairings -m benchmark

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

Source code in tests/benchmark/test_worst_compute.py
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
def test_amortized_bn128_pairings(
    benchmark_test: BenchmarkTestFiller,
    fork: Fork,
    gas_benchmark_value: int,
) -> None:
    """Test running a block with as many BN128 pairings as possible."""
    base_cost = 45_000
    pairing_cost = 34_000
    size_per_pairing = 192

    gsc = fork.gas_costs()
    intrinsic_gas_calculator = fork.transaction_intrinsic_cost_calculator()
    mem_exp_gas_calculator = fork.memory_expansion_gas_calculator()

    # 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 = (gas_benchmark_value - 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 = gas_benchmark_value - 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),
        )

        # This is ignoring "glue" opcodes, but helps to have a rough idea of
        # the right cutting point.
        approx_gas_cost_per_call = gsc.G_WARM_ACCOUNT_ACCESS + 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, 0x08, 0, Op.CALLDATASIZE, 0, 0))

    benchmark_test(
        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 case is only parametrized by fork.

Test ID (Abbreviated)
...fork_Prague-blockchain_test
...fork_Osaka-blockchain_test