Skip to content

test_keccak_max_permutations()

Documentation for tests/benchmark/compute/instruction/test_keccak.py::test_keccak_max_permutations@892e6d1e.

Generate fixtures for these test cases for Amsterdam with:

fill -v tests/benchmark/compute/instruction/test_keccak.py::test_keccak_max_permutations --gas-benchmark-values 1

Benchmark KECCAK256 instruction to maximize permutations per block.

Source code in tests/benchmark/compute/instruction/test_keccak.py
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
def test_keccak_max_permutations(
    benchmark_test: BenchmarkTestFiller,
    fork: Fork,
    tx_gas_limit: int,
) -> None:
    """Benchmark KECCAK256 instruction to maximize permutations per block."""
    # Intrinsic gas cost is paid once.
    intrinsic_gas_calculator = fork.transaction_intrinsic_cost_calculator()
    available_gas = tx_gas_limit - intrinsic_gas_calculator()

    mem_exp_gas_calculator = fork.memory_expansion_gas_calculator()

    # Discover the optimal input size to maximize keccak-permutations,
    # not to maximize keccak calls.
    # The complication of the discovery arises from
    # the non-linear gas cost of memory expansion.
    max_keccak_perm_per_block = 0
    optimal_input_length = 0
    for i in range(1, 1_000_000, 32):
        # Iteration cost disregarding memory expansion
        iteration_bytecode = Op.POP(Op.SHA3(Op.PUSH0, Op.DUP1, data_size=i))
        iteration_gas_cost = iteration_bytecode.gas_cost(fork)
        # From the available gas, we subtract the mem expansion costs
        # considering we know the current input size length i.
        available_gas_after_expansion = max(
            0, available_gas - mem_exp_gas_calculator(new_bytes=i)
        )
        # Calculate how many calls we can do.
        num_keccak_calls = available_gas_after_expansion // iteration_gas_cost
        # KECCAK does 1 permutation every 136 bytes.
        num_keccak_permutations = num_keccak_calls * math.ceil(i / KECCAK_RATE)

        # If we found an input size that is better (reg permutations/gas), then
        # save it.
        if num_keccak_permutations > max_keccak_perm_per_block:
            max_keccak_perm_per_block = num_keccak_permutations
            optimal_input_length = i

    benchmark_test(
        target_opcode=Op.SHA3,
        code_generator=JumpLoopGenerator(
            setup=Op.PUSH20[optimal_input_length],
            attack_block=Op.POP(
                Op.SHA3(Op.PUSH0, Op.DUP1, data_size=optimal_input_length)
            ),
        ),
    )

Parametrized Test Cases

This test generates 1 parametrized test case across 3 forks.