20
20
KECCAK_RATE = 136
21
21
22
22
23
- @pytest .mark .zkevm
24
- @pytest .mark .valid_from ("Cancun" )
25
- @pytest .mark .parametrize (
26
- "gas_limit" ,
27
- [
28
- 36_000_000 ,
29
- 60_000_000 ,
30
- 100_000_000 ,
31
- 300_000_000 ,
32
- ],
33
- )
34
- def test_worst_keccak (
35
- blockchain_test : BlockchainTestFiller ,
36
- pre : Alloc ,
37
- fork : Fork ,
38
- gas_limit : int ,
39
- ):
40
- """Test running a block with as many KECCAK256 permutations as possible."""
41
- env = Environment (gas_limit = gas_limit )
42
-
43
- # Intrinsic gas cost is paid once.
44
- intrinsic_gasc_calculator = fork .transaction_intrinsic_cost_calculator ()
45
- available_gas = gas_limit - intrinsic_gasc_calculator ()
46
-
47
- gsc = fork .gas_costs ()
48
- mem_exp_gas_calculator = fork .memory_expansion_gas_calculator ()
49
-
50
- # Discover the optimal input size to maximize keccak-permutations, not keccak calls.
51
- # The complication of the discovery arises from the non-linear gas cost of memory expansion.
52
- max_keccak_perm_per_block = 0
53
- optimal_input_length = 0
54
- for i in range (1 , 1_000_000 , 32 ):
55
- iteration_gas_cost = (
56
- 2 * gsc .G_VERY_LOW # PUSHN + PUSH1
57
- + gsc .G_KECCAK_256 # KECCAK256 static cost
58
- + math .ceil (i / 32 ) * gsc .G_KECCAK_256_WORD # KECCAK256 dynamic cost
59
- + gsc .G_BASE # POP
60
- )
61
- available_gas_after_expansion = max (0 , available_gas - mem_exp_gas_calculator (new_bytes = i ))
62
- num_keccak_calls = available_gas_after_expansion // iteration_gas_cost
63
- num_keccak_permutations = num_keccak_calls * math .ceil (i / KECCAK_RATE )
64
-
65
- if num_keccak_permutations > max_keccak_perm_per_block :
66
- max_keccak_perm_per_block = num_keccak_permutations
67
- optimal_input_length = i
68
-
69
- # max_iters_loop contains how many keccak calls can be done per loop.
70
- # The loop is as big as possible bounded by the maximum code size.
71
- #
72
- # The loop structure is: JUMPDEST + [attack iteration] + PUSH0 + JUMP
73
- #
74
- # Now calculate available gas for [attack iteration]:
75
- # Numerator = MAX_CODE_SIZE-3. The -3 is for the JUMPDEST, PUSH0 and JUMP.
76
- # Denominator = (PUSHN + PUSH1 + KECCAK256 + POP) + PUSH1_DATA + PUSHN_DATA
77
- # TODO: the testing framework uses PUSH1(0) instead of PUSH0 which is suboptimal for the
78
- # attack, whenever this is fixed adjust accordingly.
79
- max_iters_loop = (MAX_CODE_SIZE - 3 ) // (4 + 1 + (optimal_input_length .bit_length () + 7 ) // 8 )
80
- code = (
81
- Op .JUMPDEST
82
- + sum ([Op .SHA3 (0 , optimal_input_length ) + Op .POP ] * max_iters_loop )
83
- + Op .PUSH0
84
- + Op .JUMP
85
- )
86
- if len (code ) > MAX_CODE_SIZE :
87
- # Must never happen, but keep it as a sanity check.
88
- raise ValueError (f"Code size { len (code )} exceeds maximum code size { MAX_CODE_SIZE } " )
89
-
90
- code_address = pre .deploy_contract (code = bytes (code ))
91
-
92
- tx = Transaction (
93
- to = code_address ,
94
- gas_limit = gas_limit ,
95
- gas_price = 10 ,
96
- sender = pre .fund_eoa (),
97
- data = [],
98
- value = 0 ,
99
- )
100
-
101
- blockchain_test (
102
- env = env ,
103
- pre = pre ,
104
- post = {},
105
- blocks = [Block (txs = [tx ])],
106
- )
107
-
108
-
109
23
@pytest .mark .zkevm
110
24
@pytest .mark .valid_from ("Cancun" )
111
25
@pytest .mark .parametrize (
@@ -127,7 +41,7 @@ def test_worst_modexp(
127
41
exp_length = 32
128
42
129
43
base = 2 ** (8 * base_mod_length ) - 1
130
- mod = 2 ** (8 * base_mod_length ) - 2 # Prevnts base == mod
44
+ mod = 2 ** (8 * base_mod_length ) - 2 # Prevents base == mod
131
45
exp = 2 ** (8 * exp_length ) - 1
132
46
133
47
# MODEXP calldata
0 commit comments