Skip to content

Commit db9ae3c

Browse files
authored
Merge pull request #1976 from kclowes/eip-2565
EIP-2565 ModExp Gas Cost
2 parents 7ac5ef2 + 0696f8b commit db9ae3c

File tree

6 files changed

+230
-51
lines changed

6 files changed

+230
-51
lines changed

eth/precompiles/modexp.py

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
from typing import (
2+
Callable,
23
Tuple,
34
)
45

56
from eth_utils import (
67
big_endian_to_int,
78
int_to_big_endian,
89
)
10+
from eth_utils.toolz import (
11+
curry,
12+
)
913

1014
from eth import constants
1115

@@ -23,8 +27,8 @@
2327
)
2428

2529

26-
def _compute_adjusted_exponent_length(exponent_length: int,
27-
first_32_exponent_bytes: bytes) -> int:
30+
def compute_adjusted_exponent_length(exponent_length: int,
31+
first_32_exponent_bytes: bytes) -> int:
2832
exponent = big_endian_to_int(first_32_exponent_bytes)
2933

3034
if exponent_length <= 32 and exponent == 0:
@@ -50,7 +54,7 @@ def _compute_complexity(length: int) -> int:
5054
return length ** 2 // 16 + 480 * length - 199680
5155

5256

53-
def _extract_lengths(data: bytes) -> Tuple[int, int, int]:
57+
def extract_lengths(data: bytes) -> Tuple[int, int, int]:
5458
# extract argument lengths
5559
base_length_bytes = pad32r(data[:32])
5660
base_length = big_endian_to_int(base_length_bytes)
@@ -64,14 +68,14 @@ def _extract_lengths(data: bytes) -> Tuple[int, int, int]:
6468
return base_length, exponent_length, modulus_length
6569

6670

67-
def _compute_modexp_gas_fee(data: bytes) -> int:
68-
base_length, exponent_length, modulus_length = _extract_lengths(data)
71+
def _compute_modexp_gas_fee_eip_198(data: bytes) -> int:
72+
base_length, exponent_length, modulus_length = extract_lengths(data)
6973

7074
first_32_exponent_bytes = zpad_right(
7175
data[96 + base_length:96 + base_length + exponent_length],
7276
to_size=min(exponent_length, 32),
7377
)[:32]
74-
adjusted_exponent_length = _compute_adjusted_exponent_length(
78+
adjusted_exponent_length = compute_adjusted_exponent_length(
7579
exponent_length,
7680
first_32_exponent_bytes,
7781
)
@@ -86,7 +90,7 @@ def _compute_modexp_gas_fee(data: bytes) -> int:
8690

8791

8892
def _modexp(data: bytes) -> int:
89-
base_length, exponent_length, modulus_length = _extract_lengths(data)
93+
base_length, exponent_length, modulus_length = extract_lengths(data)
9094

9195
if base_length == 0:
9296
return 0
@@ -121,18 +125,22 @@ def _modexp(data: bytes) -> int:
121125
return result
122126

123127

124-
def modexp(computation: BaseComputation) -> BaseComputation:
128+
@curry
129+
def modexp(
130+
computation: BaseComputation,
131+
gas_calculator: Callable[[bytes], int] = _compute_modexp_gas_fee_eip_198
132+
) -> BaseComputation:
125133
"""
126134
https://github.com/ethereum/EIPs/pull/198
127135
"""
128136
data = computation.msg.data_as_bytes
129137

130-
gas_fee = _compute_modexp_gas_fee(data)
138+
gas_fee = gas_calculator(data)
131139
computation.consume_gas(gas_fee, reason='MODEXP Precompile')
132140

133141
result = _modexp(data)
134142

135-
_, _, modulus_length = _extract_lengths(data)
143+
_, _, modulus_length = extract_lengths(data)
136144

137145
# Modulo 0 is undefined, return zero
138146
# https://math.stackexchange.com/questions/516251/why-is-n-mod-0-undefined

eth/vm/forks/berlin/computation.py

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,23 @@
1+
import math
2+
3+
from eth_utils.toolz import (
4+
merge,
5+
)
6+
7+
from eth.precompiles.modexp import (
8+
compute_adjusted_exponent_length,
9+
extract_lengths,
10+
modexp,
11+
)
12+
from eth._utils.address import (
13+
force_bytes_to_address,
14+
)
15+
from eth._utils.padding import (
16+
zpad_right,
17+
)
18+
19+
from eth.vm.forks.berlin import constants
20+
121
from eth.vm.forks.muir_glacier.computation import (
222
MUIR_GLACIER_PRECOMPILES
323
)
@@ -7,7 +27,35 @@
727

828
from .opcodes import BERLIN_OPCODES
929

10-
BERLIN_PRECOMPILES = MUIR_GLACIER_PRECOMPILES
30+
31+
def _calculate_multiplication_complexity(base_length: int, modulus_length: int) -> int:
32+
max_length = max(base_length, modulus_length)
33+
words = math.ceil(max_length / 8)
34+
return words**2
35+
36+
37+
def _compute_modexp_gas_fee_eip_2565(data: bytes) -> int:
38+
base_length, exponent_length, modulus_length = extract_lengths(data)
39+
40+
base_end_idx = 96 + base_length
41+
exponent_end_idx = base_end_idx + exponent_length
42+
43+
exponent_bytes = zpad_right(
44+
data[base_end_idx:exponent_end_idx],
45+
to_size=exponent_length,
46+
)
47+
48+
multiplication_complexity = _calculate_multiplication_complexity(base_length, modulus_length)
49+
iteration_count = compute_adjusted_exponent_length(exponent_length, exponent_bytes)
50+
return max(200,
51+
multiplication_complexity * iteration_count
52+
// constants.GAS_MOD_EXP_QUADRATIC_DENOMINATOR_EIP_2565)
53+
54+
55+
BERLIN_PRECOMPILES = merge(
56+
MUIR_GLACIER_PRECOMPILES,
57+
{force_bytes_to_address(b'\x05'): modexp(gas_calculator=_compute_modexp_gas_fee_eip_2565)},
58+
)
1159

1260

1361
class BerlinComputation(MuirGlacierComputation):

eth/vm/forks/berlin/constants.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
GAS_MOD_EXP_QUADRATIC_DENOMINATOR_EIP_2565 = 3

newsfragments/1976.feature.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Implement EIP-2565: Update ModExp precompile gas cost calculation

0 commit comments

Comments
 (0)