diff --git a/EIPS/eip-2565.md b/EIPS/eip-2565.md index a36e2a3224c635..2ad443c00798aa 100644 --- a/EIPS/eip-2565.md +++ b/EIPS/eip-2565.md @@ -1,10 +1,10 @@ --- eip: 2565 -title: Repricing of the EIP-198 ModExp precompile +title: ModExp Gas Cost author: Kelly Olson (@ineffectualproperty), Sean Gulley (@sean-sn), Simon Peffers (@simonatsn), Justin Drake (@justindrake), Dankrad Feist (@dankrad) discussions-to: https://ethereum-magicians.org/t/big-integer-modular-exponentiation-eip-198-gas-cost/4150 status: Last Call -review-period-end: 2020-07-31 +review-period-end: 2020-10-09 type: Standards Track category: Core created: 2020-03-20 @@ -12,26 +12,40 @@ requires: 198 --- ## Simple Summary -The [EIP-198](./eip-198.md) ‘big integer modular exponentiation’, or `ModExp`, precompile is currently overpriced. Re-pricing this precompile will enable more cost efficient verification of RSA signatures, verifiable delay functions (VDFs), primality checks, and more. +Defines the gas cost of the `ModExp` (`0x00..05`) precompile. ## Abstract -After benchmarking the ModExp precompile, we discovered that it is ‘overpriced’ relative to other precompiles. We also discovered that the current gas pricing formula could be improved to better estimate the computational complexity of various ModExp input variables. To improve the gas cost pricing for this precompile the following options are available: - -1. Modifying the gas pricing formula to better reflect the computational complexity of ModExp operations -2. Changing the value of the `GQUADDIVISOR` parameter in the ModExp pricing formula to bring its costs more in-line with other precompiles -3. Improving the underlying libraries beneath the ModExp Precompile -4. Any combination of (1), (2), and (3) - -We recommend **Option (1) and (2)** which provides a large practical improvement to gas estimation while keeping implementation complexity low. Option (3) can be implemented at a later point to improve the gas pricing even further. +To accurately reflect the real world operational cost of the `ModExp` precompile, this EIP specifies an algorithm for calculating the gas cost. This algorithm approximates the multiplication complexity cost and multiplies that by an approximation of the iterations required to execute the exponentiation. ## Motivation Modular exponentiation is a foundational arithmetic operation for many cryptographic functions including signatures, VDFs, SNARKs, accumulators, and more. Unfortunately, the ModExp precompile is currently over-priced, making these operations inefficient and expensive. By reducing the cost of this precompile, these cryptographic functions become more practical, enabling improved security, stronger randomness (VDFs), and more. ## Specification -The current gas pricing formula is defined in EIP-198. This formula divides a ‘computational complexity’ function by a ‘gas conversion’ parameter called `GQUADDIVISOR` to arrive at a gas cost: `floor(mult_complexity(x)/GQUADDIVISOR)` +As of `FORK_BLOCK_NUMBER`, the gas cost of calling the precompile at address `0x0000000000000000000000000000000000000005` will be calculated as follows: +``` +def calculate_multiplication_complexity(base_length, modulus_length): + max_length = max(base_length, modulus_length) + words = math.ceil(max_length / 8) + return words**2 + +def calculate_iteration_count(exponent_length, exponent): + iteration_count = 0 + if exponent_length <= 32 and exponent == 0: iteration_count = 0 + elif exponent_length <= 32: iteration_count = exponent.bit_length() - 1 + elif exponent_length > 32: iteration_count = (8 * (exponent_length - 32)) + ((exponent & (2**256 - 1)).bit_length() - 1) + return max(iteration_count, 1) + +def calculate_gas_cost(base_length, modulus_length, exponent_length, exponent): + multiplication_complexity = calculate_multiplication_complexity(base_length, modulus_length) + iteration_count = calculate_iteration_count(exponent_length, exponent) + return max(200, math.floor(multiplication_complexity * iteration_count / 3)) +``` + +## Rationale +After benchmarking the ModExp precompile, we discovered that it is ‘overpriced’ relative to other precompiles. We also discovered that the current gas pricing formula could be improved to better estimate the computational complexity of various ModExp input variables. The following changes improve the accuracy of the `ModExp` pricing: -### **Recommended** Option (1): Modify ‘computational complexity’ function and add minimum gas cost -The current complexity function, as defined in EIP-198 is as follow: +### 1. Modify ‘computational complexity’ formula to better reflect the computational complexity +The complexity function defined in [EIP-198](./eip-198.md) is as follow: ``` def mult_complexity(x): @@ -41,48 +55,45 @@ def mult_complexity(x): ``` where is `x` is `max(length_of_MODULUS, length_of_BASE)` -This complexity formula was meant to approximate the difficulty of Karatsuba multiplication. However, we found a better approximation for modelling modular exponentiation. We recommend the following formula to better estimate the computational complexity for varying input values: - -``` -def mult_complexity(x): - ceiling(x/64)^2 -``` -where is `x` is `max(length_of_MODULUS, length_of_BASE)`. `x` is divided by 64 to account for the number of limbs in multiprecision arithmetic. - -You can find comparison of these two complexity fomulas for the current test vectors as the following [spreadsheet](https://docs.google.com/spreadsheets/d/1-xBzA-2-l2ZQDQ1eh3XXGZjcRSBQ_Hnp7NubXpbiSUY/edit?usp=sharing). - -In addition to modifying the `mult_complexity` formula as above, we also recommend wrapping the entire function with a minimum gas price of 100 to ensure that a minimum amount of gas is used when the precompile is called e.g. `max(100,floor(mult_complexity(x)/GQUADDIVISOR))` - -### **Recommended** Option (2): Change value of GQUADDIVISOR -`GQUADDIVISOR` is set to `20` per EIP-198. We recommend changing the value of this parameter to `3` to account for the changes in the recommended 'computational complexity' formula above. - -### Option (3): Replace libraries used by ModExp precompiles -ModExp benchmarks for different libraries can be found at the following [spreadsheet](https://docs.google.com/spreadsheets/d/1Fq3d3wUjGN0R_FX-VPj7TKhCK33ac--P4QXB9MPQ8iw/edit?usp=sharing). - -While alternative libraries can provide improved a further 2-5x improvement in performance, this option is not recommended at this time. - -## Rationale - -### **Recommended** Option (1): Modify ‘computational complexity’ formula -A comparison of the current ‘complexity’ function and the proposed function described above can be found at the following [spreadsheet](https://docs.google.com/spreadsheets/d/1-xBzA-2-l2ZQDQ1eh3XXGZjcRSBQ_Hnp7NubXpbiSUY/edit?usp=sharing). +The complexity formula in [EIP-198](./eip-198.md) was meant to approximate the difficulty of Karatsuba multiplication. However, we found a better approximation for modelling modular exponentiation. In the complexity formula defined in this EIP, `x` is divided by 8 to account for the number of limbs in multiprecision arithmetic. A comparison of the current ‘complexity’ function and the proposed function against the execution time can be seen below: ![Option 1 Graph](../assets/eip-2565/Complexity_Regression.png) -The new complexity function has a better fit vs. the execution time when compared to the current complexity function. This better fit is because the new complexity formula accounts for the use of binary exponentiation algorithms that are used by ‘bigint’ libraries for large exponents. You may also notice the regression line of the proposed complexity function bisects the test vector data points. This is because the run time varies depending on if the modulus is even or odd. +The complexity function defined here has a better fit vs. the execution time when compared to the [EIP-198](./eip-198.md) complexity function. This better fit is because this complexity formula accounts for the use of binary exponentiation algorithms that are used by ‘bigint’ libraries for large exponents. You may also notice the regression line of the proposed complexity function bisects the test vector data points. This is because the run time varies depending on if the modulus is even or odd. -### **Recommended** Option (2): Change value of GQUADDIVISOR: -After changing the 'computational complexity' formula it is necessary to change `QGUADDIVSOR` to bring the gas costs inline with their runtime. We recommend changing the value from '20' to '3'. With this change, the cost of the ModExp precompile will have a higher cost (gas/second) than other precompiles such as ECRecover. +### 2. Change the value of GQUADDIVISOR +After changing the 'computational complexity' formula in [EIP-198](./eip-198.md) to the one defined here it is necessary to change `QGUADDIVSOR` to bring the gas costs inline with their runtime. By setting the `QGUADDIVISOR` to `3` the cost of the ModExp precompile will have a higher cost (gas/second) than other precompiles such as ECRecover. ![Option 2 Graph](../assets/eip-2565/GQuad_Change.png) -### Option (3): Improving the ModExp precompile implementations - -![Option 3 Graph](../assets/eip-2565/Library_Benchmarks.png) - -Replacing the underlying library can improve the performance of the ModExp precompile by 2x-4x for large exponents, but comes at a high implementation cost. We do not recommend this option at this time. +### 3. Set a minimum gas cost to prevent abuse +This prevents the precompile from underpricing small input values. ## Test Cases -There are no changes to the underlying interface or arithmetic algorithms, so the existing test vectors can be reused. Gas values will need to be updated in the existing unit tests based on the final pricing decided in this EIP. This will ensure that the updated gas calculations are done correctly. +There are no changes to the underlying interface or arithmetic algorithms, so the existing test vectors can be reused. Below is a table with the updated test vectors: + +| Test Case | EIP-198 Pricing | EIP-2565 Pricing | +| ------------- | ------------- | ------------- | +| modexp_nagydani_1_square | 204 | 200 | +| modexp_nagydani_1_qube | 204 | 200 | +| modexp_nagydani_1_pow0x10001 | 3276 | 341 | +| modexp_nagydani_2_square | 665 | 200 | +| modexp_nagydani_2_qube | 665 | 200 | +| modexp_nagydani_2_pow0x10001 | 10649 | 1365 | +| modexp_nagydani_3_square | 1894 | 341 | +| modexp_nagydani_3_qube | 1894 | 341 | +| modexp_nagydani_3_pow0x10001 | 30310 | 5461 | +| modexp_nagydani_4_square | 5580 | 1365 | +| modexp_nagydani_4_qube | 5580 | 1365 | +| modexp_nagydani_4_pow0x10001 | 89292 | 21845 | +| modexp_nagydani_5_square | 17868 | 5461 | +| modexp_nagydani_5_qube | 17868 | 5461 | +| modexp_nagydani_5_pow0x10001 | 285900 | 87381 | + +## Implementations +[Geth](https://github.com/ethereum/go-ethereum/pull/21607) + +[Python](https://gist.github.com/ineffectualproperty/60e34f15c31850c5b60c8cf3a28cd423) ## Security Considerations The biggest security consideration for this EIP is creating a potential DoS vector by making ModExp operations too inexpensive relative to their computation time. diff --git a/assets/eip-2565/Complexity_Regression.png b/assets/eip-2565/Complexity_Regression.png index 236f9dc35d31f6..059671bbecf6fe 100644 Binary files a/assets/eip-2565/Complexity_Regression.png and b/assets/eip-2565/Complexity_Regression.png differ diff --git a/assets/eip-2565/GQuad_Change.png b/assets/eip-2565/GQuad_Change.png index 92914f002e369b..53bb641124b985 100644 Binary files a/assets/eip-2565/GQuad_Change.png and b/assets/eip-2565/GQuad_Change.png differ diff --git a/assets/eip-2565/Library_Benchmarks.png b/assets/eip-2565/Library_Benchmarks.png deleted file mode 100644 index fa9c9235546c6c..00000000000000 Binary files a/assets/eip-2565/Library_Benchmarks.png and /dev/null differ