├── .gitattributes ├── .github └── workflows │ ├── node.js.yml │ └── python-app.yml ├── .gitignore ├── .prettierignore ├── .prettierrc ├── README.md ├── buidler.config.ts ├── contracts ├── AddChainLong.sol ├── ModExp.sol ├── Monster.sol └── TestModExp.sol ├── magic_codegen.py ├── modexp.py ├── package-lock.json ├── package.json ├── reprice.py ├── requirements.txt ├── test └── modexp.test.ts └── tsconfig.json /.gitattributes: -------------------------------------------------------------------------------- 1 | *.sol linguist-language=Solidity -------------------------------------------------------------------------------- /.github/workflows/node.js.yml: -------------------------------------------------------------------------------- 1 | # This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions 3 | 4 | name: Node.js CI 5 | 6 | on: 7 | push: 8 | branches: [ master ] 9 | pull_request: 10 | branches: [ master ] 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | strategy: 18 | matrix: 19 | node-version: [12.x] 20 | 21 | steps: 22 | - uses: actions/checkout@v2 23 | - name: Use Node.js ${{ matrix.node-version }} 24 | uses: actions/setup-node@v1 25 | with: 26 | node-version: ${{ matrix.node-version }} 27 | - run: npm ci 28 | - run: npm test 29 | -------------------------------------------------------------------------------- /.github/workflows/python-app.yml: -------------------------------------------------------------------------------- 1 | # This workflow will install Python dependencies, run tests and lint with a single version of Python 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions 3 | 4 | name: Python application 5 | 6 | on: 7 | push: 8 | branches: [ master ] 9 | pull_request: 10 | branches: [ master ] 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v2 19 | - name: Set up Python 3.8 20 | uses: actions/setup-python@v2 21 | with: 22 | python-version: 3.8 23 | - name: Install dependencies 24 | run: | 25 | python -m pip install --upgrade pip 26 | pip install -r requirements.txt 27 | - name: Test with pytest 28 | run: | 29 | pytest magic_codegen.py -s 30 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | venv 3 | 4 | #Buidler files 5 | cache 6 | artifacts 7 | 8 | .python-version 9 | __pycache__/ -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | artifacts/ 2 | cache/ 3 | package-lock.json 4 | .github/ 5 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "overrides": [ 3 | { 4 | "files": ["*.sol", "*.ts", "*.js"], 5 | "options": { 6 | "printWidth": 120, 7 | "tabWidth": 4, 8 | "useTabs": false, 9 | "singleQuote": false, 10 | "bracketSpacing": true 11 | } 12 | } 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ModExp in Solidity 2 | 3 | ![Node.js CI](https://github.com/ChihChengLiang/modexp/workflows/Node.js%20CI/badge.svg) 4 | 5 | The modular exponentiation is a function that calculates the math expression `a^b % n`. For example, let a=5, b=3, and c=4, the expression evaluates `5^3 % 4 = 125 % 4 = 1`. 6 | 7 | We explore the case that given the exponent `b` and the modulus `n` are fixed known numbers, how good we can perform in Solidity language. This is useful when we need to perform the hash to curve operation to validate the aggregated signature on chain. 8 | 9 | We use the parameters of BN254 curve throughout the examples. In its hash to curve operation, we check if an input `x` has a square root on the G1 subgroup. A modexp is calculated with exponent `(n+1)/4` and modulus `n` known before the contract is deployed. 10 | 11 | ```python 12 | n = 0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47 13 | 14 | def modexp(x): 15 | return pow(x, (n + 1) // 4, n) 16 | ``` 17 | 18 | ## Test and Benchmark 19 | 20 | ```bash 21 | > npm run test 22 | 23 | Ts implementation 24 | ✓ works at some inputs (50ms) 25 | 26 | ModExp 27 | Deployment gas 628448 28 | Avg cost 13750 29 | ✓ baseline: Calling EIP-198 precompile (426ms) 30 | Avg cost 38673 31 | ✓ modexp 1: Naive Solidity (1203ms) 32 | Avg cost 30470 33 | ✓ modexp 2: Naive inline assembly (551ms) 34 | Avg cost 23981 35 | ✓ modexp 3: Loop unroll (489ms) 36 | Avg cost 22847 37 | ✓ modexp 4: Minor optimize (373ms) 38 | Avg cost 21179 39 | ✓ modexp 5: Unroll more (358ms) 40 | Avg cost 21021 41 | ✓ modexp 6: Remove if statement (355ms) 42 | Avg cost 26489 43 | ✓ modexp 7: Reproduce historical result (342ms) 44 | 45 | ModExp Monster code 46 | Deployment gas 504279 47 | Avg cost 7133 48 | ✓ modexp 8: monster code (257ms) 49 | 50 | ModExp AddChainLong 51 | Deployment gas 499965 52 | Avg cost 6744 53 | ✓ modexp 9: add chain long (233ms) 54 | ``` 55 | 56 | Test magic_codegen 57 | 58 | ``` 59 | pytest magic_codegen.py -s 60 | ``` 61 | Proto's magic codegen optimized the Monster code at bytecode level and has a gas cost 5075. 62 | -------------------------------------------------------------------------------- /buidler.config.ts: -------------------------------------------------------------------------------- 1 | import { usePlugin } from "@nomiclabs/buidler/config"; 2 | 3 | usePlugin("@nomiclabs/buidler-waffle"); 4 | 5 | // You have to export an object to set up your config 6 | // This object can have the following optional entries: 7 | // defaultNetwork, networks, solc, and paths. 8 | // Go to https://buidler.dev/config/ to learn more 9 | export default { 10 | // This is a sample solc configuration that specifies which version of solc to use 11 | solc: { 12 | version: "0.6.8", 13 | }, 14 | }; 15 | -------------------------------------------------------------------------------- /contracts/AddChainLong.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.6.8; 3 | 4 | contract AddChainLong { 5 | function test_modexp(uint256 x) public view returns (uint256 xx, uint256 gasCost) { 6 | gasCost = gasleft(); 7 | xx = modexp(x); 8 | gasCost = gasCost - gasleft(); 9 | } 10 | 11 | // Thank kobigurk for the contribution to this function 12 | // The idea is since a shorter add chain uses more variables and the stack size is not deep enough 13 | // we run add chain but choose a longer chain to fit stack size 14 | // go get -u github.com/kobigurk/addchain/ 15 | // addchain 5472060717959818805561601436314318772174077789324455915672259473661306552146 16 | function modexp(uint256 t6) internal pure returns (uint256 t0) { 17 | assembly { 18 | let n := 0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47 19 | 20 | t0 := mulmod(t6, t6, n) 21 | let t4 := mulmod(t0, t6, n) 22 | let t2 := mulmod(t4, t0, n) 23 | let t3 := mulmod(t4, t4, n) 24 | let t8 := mulmod(t2, t0, n) 25 | let t1 := mulmod(t3, t4, n) 26 | let t5 := mulmod(t3, t2, n) 27 | t0 := mulmod(t3, t3, n) 28 | let t7 := mulmod(t8, t3, n) 29 | t3 := mulmod(t1, t3, n) 30 | t0 := mulmod(t0, t0, n) 31 | t0 := mulmod(t0, t0, n) 32 | t0 := mulmod(t0, t0, n) 33 | t0 := mulmod(t0, t0, n) 34 | t0 := mulmod(t0, t0, n) 35 | t0 := mulmod(t0, t4, n) 36 | t0 := mulmod(t0, t0, n) 37 | t0 := mulmod(t0, t0, n) 38 | t0 := mulmod(t0, t0, n) 39 | t0 := mulmod(t0, t6, n) 40 | t0 := mulmod(t0, t0, n) 41 | t0 := mulmod(t0, t0, n) 42 | t0 := mulmod(t0, t0, n) 43 | t0 := mulmod(t0, t0, n) 44 | t0 := mulmod(t0, t6, n) 45 | t0 := mulmod(t0, t0, n) 46 | t0 := mulmod(t0, t0, n) 47 | t0 := mulmod(t0, t0, n) 48 | t0 := mulmod(t0, t0, n) 49 | t0 := mulmod(t0, t0, n) 50 | t0 := mulmod(t0, t8, n) 51 | t0 := mulmod(t0, t0, n) 52 | t0 := mulmod(t0, t0, n) 53 | t0 := mulmod(t0, t0, n) 54 | t0 := mulmod(t0, t0, n) 55 | t0 := mulmod(t0, t0, n) 56 | t0 := mulmod(t0, t8, n) 57 | t0 := mulmod(t0, t0, n) 58 | t0 := mulmod(t0, t0, n) 59 | t0 := mulmod(t0, t0, n) 60 | t0 := mulmod(t0, t6, n) 61 | t0 := mulmod(t0, t0, n) 62 | t0 := mulmod(t0, t0, n) 63 | t0 := mulmod(t0, t0, n) 64 | t0 := mulmod(t0, t0, n) 65 | t0 := mulmod(t0, t8, n) 66 | t0 := mulmod(t0, t0, n) 67 | t0 := mulmod(t0, t0, n) 68 | t0 := mulmod(t0, t0, n) 69 | t0 := mulmod(t0, t0, n) 70 | t0 := mulmod(t0, t0, n) 71 | t0 := mulmod(t0, t6, n) 72 | t0 := mulmod(t0, t0, n) 73 | t0 := mulmod(t0, t0, n) 74 | t0 := mulmod(t0, t0, n) 75 | t0 := mulmod(t0, t0, n) 76 | t0 := mulmod(t0, t4, n) 77 | t0 := mulmod(t0, t0, n) 78 | t0 := mulmod(t0, t0, n) 79 | t0 := mulmod(t0, t0, n) 80 | t0 := mulmod(t0, t0, n) 81 | t0 := mulmod(t0, t0, n) 82 | t0 := mulmod(t0, t0, n) 83 | t0 := mulmod(t0, t0, n) 84 | t0 := mulmod(t0, t7, n) 85 | t0 := mulmod(t0, t0, n) 86 | t0 := mulmod(t0, t0, n) 87 | t0 := mulmod(t0, t0, n) 88 | t0 := mulmod(t0, t0, n) 89 | t0 := mulmod(t0, t0, n) 90 | t0 := mulmod(t0, t0, n) 91 | t0 := mulmod(t0, t0, n) 92 | t0 := mulmod(t0, t0, n) 93 | t0 := mulmod(t0, t0, n) 94 | t0 := mulmod(t0, t0, n) 95 | t0 := mulmod(t0, t2, n) 96 | t0 := mulmod(t0, t0, n) 97 | t0 := mulmod(t0, t0, n) 98 | t0 := mulmod(t0, t0, n) 99 | t0 := mulmod(t0, t0, n) 100 | t0 := mulmod(t0, t4, n) 101 | t0 := mulmod(t0, t0, n) 102 | t0 := mulmod(t0, t0, n) 103 | t0 := mulmod(t0, t0, n) 104 | t0 := mulmod(t0, t0, n) 105 | t0 := mulmod(t0, t8, n) 106 | t0 := mulmod(t0, t0, n) 107 | t0 := mulmod(t0, t0, n) 108 | t0 := mulmod(t0, t0, n) 109 | t0 := mulmod(t0, t0, n) 110 | t0 := mulmod(t0, t0, n) 111 | t0 := mulmod(t0, t0, n) 112 | t0 := mulmod(t0, t0, n) 113 | t0 := mulmod(t0, t2, n) 114 | t0 := mulmod(t0, t0, n) 115 | t0 := mulmod(t0, t0, n) 116 | t0 := mulmod(t0, t0, n) 117 | t0 := mulmod(t0, t0, n) 118 | t0 := mulmod(t0, t0, n) 119 | t0 := mulmod(t0, t0, n) 120 | t0 := mulmod(t0, t6, n) 121 | t0 := mulmod(t0, t0, n) 122 | t0 := mulmod(t0, t0, n) 123 | t0 := mulmod(t0, t0, n) 124 | t0 := mulmod(t0, t0, n) 125 | t0 := mulmod(t0, t0, n) 126 | t0 := mulmod(t0, t0, n) 127 | t0 := mulmod(t0, t0, n) 128 | t0 := mulmod(t0, t5, n) 129 | t0 := mulmod(t0, t0, n) 130 | t0 := mulmod(t0, t0, n) 131 | t0 := mulmod(t0, t0, n) 132 | t0 := mulmod(t0, t0, n) 133 | t0 := mulmod(t0, t0, n) 134 | t0 := mulmod(t0, t7, n) 135 | t0 := mulmod(t0, t0, n) 136 | t0 := mulmod(t0, t0, n) 137 | t0 := mulmod(t0, t0, n) 138 | t0 := mulmod(t0, t2, n) 139 | t0 := mulmod(t0, t0, n) 140 | t0 := mulmod(t0, t0, n) 141 | t0 := mulmod(t0, t0, n) 142 | t0 := mulmod(t0, t0, n) 143 | t0 := mulmod(t0, t0, n) 144 | t0 := mulmod(t0, t0, n) 145 | t0 := mulmod(t0, t0, n) 146 | t0 := mulmod(t0, t0, n) 147 | t0 := mulmod(t0, t4, n) 148 | t0 := mulmod(t0, t0, n) 149 | t0 := mulmod(t0, t0, n) 150 | t0 := mulmod(t0, t0, n) 151 | t0 := mulmod(t0, t0, n) 152 | t0 := mulmod(t0, t0, n) 153 | t0 := mulmod(t0, t0, n) 154 | t0 := mulmod(t0, t0, n) 155 | t0 := mulmod(t0, t0, n) 156 | t0 := mulmod(t0, t0, n) 157 | t0 := mulmod(t0, t2, n) 158 | t0 := mulmod(t0, t0, n) 159 | t0 := mulmod(t0, t0, n) 160 | t0 := mulmod(t0, t0, n) 161 | t0 := mulmod(t0, t4, n) 162 | t0 := mulmod(t0, t0, n) 163 | t0 := mulmod(t0, t0, n) 164 | t0 := mulmod(t0, t0, n) 165 | t0 := mulmod(t0, t0, n) 166 | t0 := mulmod(t0, t0, n) 167 | t0 := mulmod(t0, t0, n) 168 | t0 := mulmod(t0, t0, n) 169 | t0 := mulmod(t0, t0, n) 170 | t0 := mulmod(t0, t5, n) 171 | t0 := mulmod(t0, t0, n) 172 | t0 := mulmod(t0, t0, n) 173 | t0 := mulmod(t0, t0, n) 174 | t0 := mulmod(t0, t0, n) 175 | t0 := mulmod(t0, t5, n) 176 | t0 := mulmod(t0, t0, n) 177 | t0 := mulmod(t0, t0, n) 178 | t0 := mulmod(t0, t0, n) 179 | t0 := mulmod(t0, t0, n) 180 | t0 := mulmod(t0, t0, n) 181 | t0 := mulmod(t0, t2, n) 182 | t0 := mulmod(t0, t0, n) 183 | t0 := mulmod(t0, t0, n) 184 | t0 := mulmod(t0, t0, n) 185 | t0 := mulmod(t0, t8, n) 186 | t0 := mulmod(t0, t0, n) 187 | t0 := mulmod(t0, t0, n) 188 | t0 := mulmod(t0, t0, n) 189 | t0 := mulmod(t0, t0, n) 190 | t0 := mulmod(t0, t0, n) 191 | t0 := mulmod(t0, t0, n) 192 | t0 := mulmod(t0, t0, n) 193 | t0 := mulmod(t0, t0, n) 194 | t0 := mulmod(t0, t0, n) 195 | t0 := mulmod(t0, t0, n) 196 | t0 := mulmod(t0, t5, n) 197 | t0 := mulmod(t0, t0, n) 198 | t0 := mulmod(t0, t0, n) 199 | t0 := mulmod(t0, t0, n) 200 | t0 := mulmod(t0, t0, n) 201 | t0 := mulmod(t0, t2, n) 202 | t0 := mulmod(t0, t0, n) 203 | t0 := mulmod(t0, t0, n) 204 | t0 := mulmod(t0, t0, n) 205 | t0 := mulmod(t0, t0, n) 206 | t0 := mulmod(t0, t0, n) 207 | t0 := mulmod(t0, t1, n) 208 | t0 := mulmod(t0, t0, n) 209 | t0 := mulmod(t0, t0, n) 210 | t0 := mulmod(t0, t0, n) 211 | t0 := mulmod(t0, t0, n) 212 | t0 := mulmod(t0, t0, n) 213 | t0 := mulmod(t0, t0, n) 214 | t0 := mulmod(t0, t0, n) 215 | t0 := mulmod(t0, t5, n) 216 | t0 := mulmod(t0, t0, n) 217 | t0 := mulmod(t0, t0, n) 218 | t0 := mulmod(t0, t6, n) 219 | t0 := mulmod(t0, t0, n) 220 | t0 := mulmod(t0, t0, n) 221 | t0 := mulmod(t0, t0, n) 222 | t0 := mulmod(t0, t0, n) 223 | t0 := mulmod(t0, t0, n) 224 | t0 := mulmod(t0, t0, n) 225 | t0 := mulmod(t0, t0, n) 226 | t0 := mulmod(t0, t8, n) 227 | t0 := mulmod(t0, t0, n) 228 | t0 := mulmod(t0, t0, n) 229 | t0 := mulmod(t0, t0, n) 230 | t0 := mulmod(t0, t0, n) 231 | t0 := mulmod(t0, t0, n) 232 | t0 := mulmod(t0, t0, n) 233 | t0 := mulmod(t0, t8, n) 234 | t0 := mulmod(t0, t0, n) 235 | t0 := mulmod(t0, t0, n) 236 | t0 := mulmod(t0, t0, n) 237 | t0 := mulmod(t0, t0, n) 238 | t0 := mulmod(t0, t0, n) 239 | t0 := mulmod(t0, t2, n) 240 | t0 := mulmod(t0, t0, n) 241 | t0 := mulmod(t0, t0, n) 242 | t0 := mulmod(t0, t6, n) 243 | t0 := mulmod(t0, t0, n) 244 | t0 := mulmod(t0, t0, n) 245 | t0 := mulmod(t0, t0, n) 246 | t0 := mulmod(t0, t0, n) 247 | t0 := mulmod(t0, t0, n) 248 | t0 := mulmod(t0, t0, n) 249 | t0 := mulmod(t0, t0, n) 250 | t0 := mulmod(t0, t7, n) 251 | t0 := mulmod(t0, t0, n) 252 | t0 := mulmod(t0, t0, n) 253 | t0 := mulmod(t0, t0, n) 254 | t0 := mulmod(t0, t0, n) 255 | t0 := mulmod(t0, t0, n) 256 | t0 := mulmod(t0, t0, n) 257 | t0 := mulmod(t0, t3, n) 258 | t0 := mulmod(t0, t0, n) 259 | t0 := mulmod(t0, t0, n) 260 | t0 := mulmod(t0, t0, n) 261 | t0 := mulmod(t0, t0, n) 262 | t0 := mulmod(t0, t0, n) 263 | t0 := mulmod(t0, t6, n) 264 | t0 := mulmod(t0, t0, n) 265 | t0 := mulmod(t0, t0, n) 266 | t0 := mulmod(t0, t0, n) 267 | t0 := mulmod(t0, t0, n) 268 | t0 := mulmod(t0, t0, n) 269 | t0 := mulmod(t0, t0, n) 270 | t0 := mulmod(t0, t6, n) 271 | t0 := mulmod(t0, t0, n) 272 | t0 := mulmod(t0, t0, n) 273 | t0 := mulmod(t0, t0, n) 274 | t0 := mulmod(t0, t0, n) 275 | t0 := mulmod(t0, t0, n) 276 | t0 := mulmod(t0, t4, n) 277 | t0 := mulmod(t0, t0, n) 278 | t0 := mulmod(t0, t0, n) 279 | t0 := mulmod(t0, t0, n) 280 | t0 := mulmod(t0, t0, n) 281 | t0 := mulmod(t0, t0, n) 282 | t0 := mulmod(t0, t0, n) 283 | t0 := mulmod(t0, t0, n) 284 | t0 := mulmod(t0, t0, n) 285 | t0 := mulmod(t0, t0, n) 286 | t0 := mulmod(t0, t5, n) 287 | t0 := mulmod(t0, t0, n) 288 | t0 := mulmod(t0, t0, n) 289 | t0 := mulmod(t0, t0, n) 290 | t0 := mulmod(t0, t4, n) 291 | t0 := mulmod(t0, t0, n) 292 | t0 := mulmod(t0, t0, n) 293 | t0 := mulmod(t0, t0, n) 294 | t0 := mulmod(t0, t4, n) 295 | t0 := mulmod(t0, t0, n) 296 | t0 := mulmod(t0, t0, n) 297 | t0 := mulmod(t0, t0, n) 298 | t0 := mulmod(t0, t0, n) 299 | t0 := mulmod(t0, t0, n) 300 | t0 := mulmod(t0, t0, n) 301 | t0 := mulmod(t0, t0, n) 302 | t0 := mulmod(t0, t0, n) 303 | t0 := mulmod(t0, t3, n) 304 | t0 := mulmod(t0, t0, n) 305 | t0 := mulmod(t0, t0, n) 306 | t0 := mulmod(t0, t0, n) 307 | t0 := mulmod(t0, t0, n) 308 | t0 := mulmod(t0, t1, n) 309 | t0 := mulmod(t0, t0, n) 310 | t0 := mulmod(t0, t0, n) 311 | t0 := mulmod(t0, t0, n) 312 | t0 := mulmod(t0, t0, n) 313 | t0 := mulmod(t0, t3, n) 314 | t0 := mulmod(t0, t0, n) 315 | t0 := mulmod(t0, t0, n) 316 | t0 := mulmod(t0, t0, n) 317 | t0 := mulmod(t0, t2, n) 318 | t0 := mulmod(t0, t0, n) 319 | t0 := mulmod(t0, t0, n) 320 | t0 := mulmod(t0, t0, n) 321 | t0 := mulmod(t0, t0, n) 322 | t0 := mulmod(t0, t0, n) 323 | t0 := mulmod(t0, t1, n) 324 | t0 := mulmod(t0, t0, n) 325 | } 326 | } 327 | } 328 | -------------------------------------------------------------------------------- /contracts/ModExp.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.6.8; 3 | 4 | library ModExp { 5 | // Baseline: calling EIP-198 precompile 6 | // https://github.com/ethereum/EIPs/blob/master/EIPS/eip-198.md 7 | // The precompile is located at the address 0x00......05 8 | // and has inputs of the following format 9 | // 10 | function baseline(uint256 x) internal view returns (uint256 xx) { 11 | assembly { 12 | let freemem := mload(0x40) 13 | // length_of_BASE: 32 bytes 14 | mstore(freemem, 0x20) 15 | // length_of_EXPONENT: 32 bytes 16 | mstore(add(freemem, 0x20), 0x20) 17 | // length_of_MODULUS: 32 bytes 18 | mstore(add(freemem, 0x40), 0x20) 19 | // BASE: The input x 20 | mstore(add(freemem, 0x60), x) 21 | // EXPONENT: (N + 1) / 4 = 0xc19139cb84c680a6e14116da060561765e05aa45a1c72a34f082305b61f3f52 22 | mstore(add(freemem, 0x80), 0xc19139cb84c680a6e14116da060561765e05aa45a1c72a34f082305b61f3f52) 23 | // MODULUS: N = 0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47 24 | mstore(add(freemem, 0xA0), 0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47) 25 | let success := staticcall( 26 | sub(gas(), 2000), 27 | // call the address 0x00......05 28 | 5, 29 | // loads the 6 * 32 bytes inputs from 30 | freemem, 31 | 0xC0, 32 | // stores the 32 bytes return at 33 | freemem, 34 | 0x20 35 | ) 36 | xx := mload(freemem) 37 | } 38 | } 39 | 40 | // Naive Solidity implementation of square and multiply 41 | function modexp(uint256 x) internal pure returns (uint256) { 42 | uint256 sq = x; 43 | uint256 xx = 1; 44 | uint256 n = 0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47; 45 | // (N + 1) / 4 = 0xc19139cb84c680a6e14116da060561765e05aa45a1c72a34f082305b61f3f52 46 | uint256 v = 0xc19139cb84c680a6e14116da060561765e05aa45a1c72a34f082305b61f3f52; 47 | for (uint256 i = 0; i < 252; i++) { 48 | if ((v >> i) & 1 == 1) { 49 | xx = mulmod(xx, sq, n); 50 | } 51 | sq = mulmod(sq, sq, n); 52 | } 53 | return xx; 54 | } 55 | 56 | // Improve with inline assembly 57 | function modexp2(uint256 x) internal pure returns (uint256 xx) { 58 | assembly { 59 | xx := 1 60 | let n := 0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47 61 | let v := 0xc19139cb84c680a6e14116da060561765e05aa45a1c72a34f082305b61f3f52 62 | for { 63 | let i := 0 64 | } lt(i, 252) { 65 | i := add(i, 1) 66 | } { 67 | if eq(and(shr(i, v), 1), 1) { 68 | xx := mulmod(xx, x, n) 69 | } 70 | x := mulmod(x, x, n) 71 | } 72 | } 73 | } 74 | 75 | // Use loop unrolling 76 | function modexp3(uint256 x) internal pure returns (uint256 xx) { 77 | assembly { 78 | xx := 1 79 | let n := 0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47 80 | let v := 0xc19139cb84c680a6e14116da060561765e05aa45a1c72a34f082305b61f3f52 81 | for { 82 | let i := 0 83 | } lt(i, 63) { 84 | i := add(i, 1) 85 | } { 86 | if eq(and(shr(mul(i, 4), v), 1), 1) { 87 | xx := mulmod(xx, x, n) 88 | } 89 | x := mulmod(x, x, n) 90 | if eq(and(shr(add(mul(i, 4), 1), v), 1), 1) { 91 | xx := mulmod(xx, x, n) 92 | } 93 | x := mulmod(x, x, n) 94 | if eq(and(shr(add(mul(i, 4), 2), v), 1), 1) { 95 | xx := mulmod(xx, x, n) 96 | } 97 | x := mulmod(x, x, n) 98 | if eq(and(shr(add(mul(i, 4), 3), v), 1), 1) { 99 | xx := mulmod(xx, x, n) 100 | } 101 | x := mulmod(x, x, n) 102 | } 103 | } 104 | } 105 | 106 | // Minor optimize 107 | function modexp4(uint256 x) internal pure returns (uint256 xx) { 108 | assembly { 109 | xx := 1 110 | 111 | let n := 0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47 112 | let v := 0xc19139cb84c680a6e14116da060561765e05aa45a1c72a34f082305b61f3f52 113 | for { 114 | let i := 0 115 | } lt(i, 63) { 116 | i := add(i, 1) 117 | } { 118 | if eq(and(v, 1), 1) { 119 | xx := mulmod(xx, x, n) 120 | } 121 | x := mulmod(x, x, n) 122 | v := shr(1, v) 123 | if eq(and(v, 1), 1) { 124 | xx := mulmod(xx, x, n) 125 | } 126 | x := mulmod(x, x, n) 127 | v := shr(1, v) 128 | if eq(and(v, 1), 1) { 129 | xx := mulmod(xx, x, n) 130 | } 131 | x := mulmod(x, x, n) 132 | v := shr(1, v) 133 | if eq(and(v, 1), 1) { 134 | xx := mulmod(xx, x, n) 135 | } 136 | x := mulmod(x, x, n) 137 | v := shr(1, v) 138 | } 139 | } 140 | } 141 | 142 | // Unroll more 143 | function modexp5(uint256 x) internal pure returns (uint256 xx) { 144 | assembly { 145 | xx := 1 146 | let i := 0 147 | let n := 0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47 148 | let v := 0xc19139cb84c680a6e14116da060561765e05aa45a1c72a34f082305b61f3f52 149 | for { 150 | 151 | } lt(i, 31) { 152 | 153 | } { 154 | if eq(and(v, 1), 1) { 155 | xx := mulmod(xx, x, n) 156 | } 157 | x := mulmod(x, x, n) 158 | v := shr(1, v) 159 | if eq(and(v, 1), 1) { 160 | xx := mulmod(xx, x, n) 161 | } 162 | x := mulmod(x, x, n) 163 | v := shr(1, v) 164 | if eq(and(v, 1), 1) { 165 | xx := mulmod(xx, x, n) 166 | } 167 | x := mulmod(x, x, n) 168 | v := shr(1, v) 169 | if eq(and(v, 1), 1) { 170 | xx := mulmod(xx, x, n) 171 | } 172 | x := mulmod(x, x, n) 173 | v := shr(1, v) 174 | if eq(and(v, 1), 1) { 175 | xx := mulmod(xx, x, n) 176 | } 177 | x := mulmod(x, x, n) 178 | v := shr(1, v) 179 | if eq(and(v, 1), 1) { 180 | xx := mulmod(xx, x, n) 181 | } 182 | x := mulmod(x, x, n) 183 | v := shr(1, v) 184 | if eq(and(v, 1), 1) { 185 | xx := mulmod(xx, x, n) 186 | } 187 | x := mulmod(x, x, n) 188 | v := shr(1, v) 189 | if eq(and(v, 1), 1) { 190 | xx := mulmod(xx, x, n) 191 | } 192 | x := mulmod(x, x, n) 193 | v := shr(1, v) 194 | i := add(i, 1) 195 | } 196 | if eq(and(v, 1), 1) { 197 | xx := mulmod(xx, x, n) 198 | } 199 | x := mulmod(x, x, n) 200 | v := shr(1, v) 201 | if eq(and(v, 1), 1) { 202 | xx := mulmod(xx, x, n) 203 | } 204 | x := mulmod(x, x, n) 205 | v := shr(1, v) 206 | if eq(and(v, 1), 1) { 207 | xx := mulmod(xx, x, n) 208 | } 209 | 210 | x := mulmod(x, x, n) 211 | v := shr(1, v) 212 | if eq(and(v, 1), 1) { 213 | xx := mulmod(xx, x, n) 214 | } 215 | } 216 | } 217 | 218 | // Remove if statement 219 | function modexp6(uint256 x) internal pure returns (uint256 xx) { 220 | assembly { 221 | xx := 1 222 | let n := 0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47 223 | let v := 0xc19139cb84c680a6e14116da060561765e05aa45a1c72a34f082305b61f3f52 224 | for { 225 | let i := 0 226 | } lt(i, 31) { 227 | i := add(i, 1) 228 | } { 229 | xx := mulmod(xx, add(1, mul(sub(x, 1), and(v, 1))), n) 230 | x := mulmod(x, x, n) 231 | xx := mulmod(xx, add(1, mul(sub(x, 1), shr(1, and(v, 2)))), n) 232 | x := mulmod(x, x, n) 233 | xx := mulmod(xx, add(1, mul(sub(x, 1), shr(2, and(v, 4)))), n) 234 | x := mulmod(x, x, n) 235 | xx := mulmod(xx, add(1, mul(sub(x, 1), shr(3, and(v, 8)))), n) 236 | x := mulmod(x, x, n) 237 | xx := mulmod(xx, add(1, mul(sub(x, 1), shr(4, and(v, 16)))), n) 238 | x := mulmod(x, x, n) 239 | xx := mulmod(xx, add(1, mul(sub(x, 1), shr(5, and(v, 32)))), n) 240 | x := mulmod(x, x, n) 241 | xx := mulmod(xx, add(1, mul(sub(x, 1), shr(6, and(v, 64)))), n) 242 | x := mulmod(x, x, n) 243 | xx := mulmod(xx, add(1, mul(sub(x, 1), shr(7, and(v, 128)))), n) 244 | x := mulmod(x, x, n) 245 | v := shr(8, v) 246 | } 247 | xx := mulmod(xx, add(1, mul(sub(x, 1), and(v, 1))), n) 248 | x := mulmod(x, x, n) 249 | xx := mulmod(xx, add(1, mul(sub(x, 1), shr(1, and(v, 2)))), n) 250 | x := mulmod(x, x, n) 251 | xx := mulmod(xx, add(1, mul(sub(x, 1), shr(2, and(v, 4)))), n) 252 | x := mulmod(x, x, n) 253 | xx := mulmod(xx, x, n) 254 | } 255 | } 256 | 257 | // Reproduce historical result 258 | // https://github.com/ethereum/serpent/blob/develop/examples/ecc/modexp.se 259 | function modexp7(uint256 x) internal pure returns (uint256 xx) { 260 | assembly { 261 | xx := 1 262 | let n := 0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47 263 | let v := 0xc19139cb84c680a6e14116da060561765e05aa45a1c72a34f082305b61f3f52 264 | let mask := shl(255, 1) 265 | for { 266 | let i := 0 267 | } lt(i, 64) { 268 | i := add(i, 1) 269 | } { 270 | xx := mulmod(mulmod(xx, xx, n), exp(x, iszero(iszero(and(v, mask)))), n) 271 | xx := mulmod(mulmod(xx, xx, n), exp(x, iszero(iszero(and(v, shr(1, mask))))), n) 272 | xx := mulmod(mulmod(xx, xx, n), exp(x, iszero(iszero(and(v, shr(2, mask))))), n) 273 | xx := mulmod(mulmod(xx, xx, n), exp(x, iszero(iszero(and(v, shr(3, mask))))), n) 274 | mask := shr(4, mask) 275 | } 276 | } 277 | } 278 | } 279 | -------------------------------------------------------------------------------- /contracts/Monster.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.6.8; 3 | 4 | contract Monster { 5 | function test_modexp(uint256 x) public view returns (uint256 xx, uint256 gasCost) { 6 | gasCost = gasleft(); 7 | xx = modexp(x); 8 | gasCost = gasCost - gasleft(); 9 | } 10 | 11 | // Run code_gen function in modexp.py to get all the operation for each bit 12 | function modexp(uint256 x) internal pure returns (uint256 xx) { 13 | assembly { 14 | xx := 1 15 | let n := 0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47 16 | xx := mulmod(mulmod(xx, xx, n), x, n) 17 | xx := mulmod(mulmod(xx, xx, n), x, n) 18 | xx := mulmod(xx, xx, n) 19 | xx := mulmod(xx, xx, n) 20 | xx := mulmod(xx, xx, n) 21 | xx := mulmod(xx, xx, n) 22 | xx := mulmod(xx, xx, n) 23 | xx := mulmod(mulmod(xx, xx, n), x, n) 24 | xx := mulmod(mulmod(xx, xx, n), x, n) 25 | xx := mulmod(xx, xx, n) 26 | xx := mulmod(xx, xx, n) 27 | xx := mulmod(mulmod(xx, xx, n), x, n) 28 | xx := mulmod(xx, xx, n) 29 | xx := mulmod(xx, xx, n) 30 | xx := mulmod(xx, xx, n) 31 | xx := mulmod(mulmod(xx, xx, n), x, n) 32 | xx := mulmod(xx, xx, n) 33 | xx := mulmod(xx, xx, n) 34 | xx := mulmod(mulmod(xx, xx, n), x, n) 35 | xx := mulmod(mulmod(xx, xx, n), x, n) 36 | xx := mulmod(mulmod(xx, xx, n), x, n) 37 | xx := mulmod(xx, xx, n) 38 | xx := mulmod(xx, xx, n) 39 | xx := mulmod(mulmod(xx, xx, n), x, n) 40 | xx := mulmod(mulmod(xx, xx, n), x, n) 41 | xx := mulmod(mulmod(xx, xx, n), x, n) 42 | xx := mulmod(xx, xx, n) 43 | xx := mulmod(xx, xx, n) 44 | xx := mulmod(mulmod(xx, xx, n), x, n) 45 | xx := mulmod(xx, xx, n) 46 | xx := mulmod(mulmod(xx, xx, n), x, n) 47 | xx := mulmod(mulmod(xx, xx, n), x, n) 48 | xx := mulmod(mulmod(xx, xx, n), x, n) 49 | xx := mulmod(xx, xx, n) 50 | xx := mulmod(xx, xx, n) 51 | xx := mulmod(xx, xx, n) 52 | xx := mulmod(xx, xx, n) 53 | xx := mulmod(mulmod(xx, xx, n), x, n) 54 | xx := mulmod(xx, xx, n) 55 | xx := mulmod(xx, xx, n) 56 | xx := mulmod(mulmod(xx, xx, n), x, n) 57 | xx := mulmod(mulmod(xx, xx, n), x, n) 58 | xx := mulmod(xx, xx, n) 59 | xx := mulmod(xx, xx, n) 60 | xx := mulmod(xx, xx, n) 61 | xx := mulmod(mulmod(xx, xx, n), x, n) 62 | xx := mulmod(mulmod(xx, xx, n), x, n) 63 | xx := mulmod(xx, xx, n) 64 | xx := mulmod(mulmod(xx, xx, n), x, n) 65 | xx := mulmod(xx, xx, n) 66 | xx := mulmod(xx, xx, n) 67 | xx := mulmod(xx, xx, n) 68 | xx := mulmod(xx, xx, n) 69 | xx := mulmod(xx, xx, n) 70 | xx := mulmod(xx, xx, n) 71 | xx := mulmod(xx, xx, n) 72 | xx := mulmod(mulmod(xx, xx, n), x, n) 73 | xx := mulmod(xx, xx, n) 74 | xx := mulmod(mulmod(xx, xx, n), x, n) 75 | xx := mulmod(xx, xx, n) 76 | xx := mulmod(xx, xx, n) 77 | xx := mulmod(mulmod(xx, xx, n), x, n) 78 | xx := mulmod(mulmod(xx, xx, n), x, n) 79 | xx := mulmod(xx, xx, n) 80 | xx := mulmod(mulmod(xx, xx, n), x, n) 81 | xx := mulmod(mulmod(xx, xx, n), x, n) 82 | xx := mulmod(mulmod(xx, xx, n), x, n) 83 | xx := mulmod(xx, xx, n) 84 | xx := mulmod(xx, xx, n) 85 | xx := mulmod(xx, xx, n) 86 | xx := mulmod(xx, xx, n) 87 | xx := mulmod(mulmod(xx, xx, n), x, n) 88 | xx := mulmod(xx, xx, n) 89 | xx := mulmod(mulmod(xx, xx, n), x, n) 90 | xx := mulmod(xx, xx, n) 91 | xx := mulmod(xx, xx, n) 92 | xx := mulmod(xx, xx, n) 93 | xx := mulmod(xx, xx, n) 94 | xx := mulmod(xx, xx, n) 95 | xx := mulmod(mulmod(xx, xx, n), x, n) 96 | xx := mulmod(xx, xx, n) 97 | xx := mulmod(xx, xx, n) 98 | xx := mulmod(xx, xx, n) 99 | xx := mulmod(mulmod(xx, xx, n), x, n) 100 | xx := mulmod(xx, xx, n) 101 | xx := mulmod(mulmod(xx, xx, n), x, n) 102 | xx := mulmod(mulmod(xx, xx, n), x, n) 103 | xx := mulmod(xx, xx, n) 104 | xx := mulmod(mulmod(xx, xx, n), x, n) 105 | xx := mulmod(mulmod(xx, xx, n), x, n) 106 | xx := mulmod(xx, xx, n) 107 | xx := mulmod(mulmod(xx, xx, n), x, n) 108 | xx := mulmod(mulmod(xx, xx, n), x, n) 109 | xx := mulmod(xx, xx, n) 110 | xx := mulmod(mulmod(xx, xx, n), x, n) 111 | xx := mulmod(xx, xx, n) 112 | xx := mulmod(xx, xx, n) 113 | xx := mulmod(xx, xx, n) 114 | xx := mulmod(xx, xx, n) 115 | xx := mulmod(xx, xx, n) 116 | xx := mulmod(xx, xx, n) 117 | xx := mulmod(mulmod(xx, xx, n), x, n) 118 | xx := mulmod(mulmod(xx, xx, n), x, n) 119 | xx := mulmod(xx, xx, n) 120 | xx := mulmod(xx, xx, n) 121 | xx := mulmod(xx, xx, n) 122 | xx := mulmod(xx, xx, n) 123 | xx := mulmod(xx, xx, n) 124 | xx := mulmod(xx, xx, n) 125 | xx := mulmod(mulmod(xx, xx, n), x, n) 126 | xx := mulmod(xx, xx, n) 127 | xx := mulmod(mulmod(xx, xx, n), x, n) 128 | xx := mulmod(xx, xx, n) 129 | xx := mulmod(mulmod(xx, xx, n), x, n) 130 | xx := mulmod(mulmod(xx, xx, n), x, n) 131 | xx := mulmod(xx, xx, n) 132 | xx := mulmod(xx, xx, n) 133 | xx := mulmod(xx, xx, n) 134 | xx := mulmod(xx, xx, n) 135 | xx := mulmod(mulmod(xx, xx, n), x, n) 136 | xx := mulmod(xx, xx, n) 137 | xx := mulmod(mulmod(xx, xx, n), x, n) 138 | xx := mulmod(mulmod(xx, xx, n), x, n) 139 | xx := mulmod(mulmod(xx, xx, n), x, n) 140 | xx := mulmod(xx, xx, n) 141 | xx := mulmod(mulmod(xx, xx, n), x, n) 142 | xx := mulmod(mulmod(xx, xx, n), x, n) 143 | xx := mulmod(xx, xx, n) 144 | xx := mulmod(xx, xx, n) 145 | xx := mulmod(mulmod(xx, xx, n), x, n) 146 | xx := mulmod(xx, xx, n) 147 | xx := mulmod(mulmod(xx, xx, n), x, n) 148 | xx := mulmod(mulmod(xx, xx, n), x, n) 149 | xx := mulmod(mulmod(xx, xx, n), x, n) 150 | xx := mulmod(mulmod(xx, xx, n), x, n) 151 | xx := mulmod(xx, xx, n) 152 | xx := mulmod(xx, xx, n) 153 | xx := mulmod(xx, xx, n) 154 | xx := mulmod(xx, xx, n) 155 | xx := mulmod(xx, xx, n) 156 | xx := mulmod(xx, xx, n) 157 | xx := mulmod(mulmod(xx, xx, n), x, n) 158 | xx := mulmod(xx, xx, n) 159 | xx := mulmod(mulmod(xx, xx, n), x, n) 160 | xx := mulmod(mulmod(xx, xx, n), x, n) 161 | xx := mulmod(xx, xx, n) 162 | xx := mulmod(mulmod(xx, xx, n), x, n) 163 | xx := mulmod(xx, xx, n) 164 | xx := mulmod(mulmod(xx, xx, n), x, n) 165 | xx := mulmod(xx, xx, n) 166 | xx := mulmod(mulmod(xx, xx, n), x, n) 167 | xx := mulmod(xx, xx, n) 168 | xx := mulmod(xx, xx, n) 169 | xx := mulmod(mulmod(xx, xx, n), x, n) 170 | xx := mulmod(xx, xx, n) 171 | xx := mulmod(xx, xx, n) 172 | xx := mulmod(xx, xx, n) 173 | xx := mulmod(mulmod(xx, xx, n), x, n) 174 | xx := mulmod(xx, xx, n) 175 | xx := mulmod(mulmod(xx, xx, n), x, n) 176 | xx := mulmod(mulmod(xx, xx, n), x, n) 177 | xx := mulmod(xx, xx, n) 178 | xx := mulmod(mulmod(xx, xx, n), x, n) 179 | xx := mulmod(xx, xx, n) 180 | xx := mulmod(xx, xx, n) 181 | xx := mulmod(xx, xx, n) 182 | xx := mulmod(xx, xx, n) 183 | xx := mulmod(mulmod(xx, xx, n), x, n) 184 | xx := mulmod(mulmod(xx, xx, n), x, n) 185 | xx := mulmod(mulmod(xx, xx, n), x, n) 186 | xx := mulmod(xx, xx, n) 187 | xx := mulmod(xx, xx, n) 188 | xx := mulmod(xx, xx, n) 189 | xx := mulmod(mulmod(xx, xx, n), x, n) 190 | xx := mulmod(mulmod(xx, xx, n), x, n) 191 | xx := mulmod(mulmod(xx, xx, n), x, n) 192 | xx := mulmod(xx, xx, n) 193 | xx := mulmod(xx, xx, n) 194 | xx := mulmod(mulmod(xx, xx, n), x, n) 195 | xx := mulmod(xx, xx, n) 196 | xx := mulmod(mulmod(xx, xx, n), x, n) 197 | xx := mulmod(xx, xx, n) 198 | xx := mulmod(mulmod(xx, xx, n), x, n) 199 | xx := mulmod(xx, xx, n) 200 | xx := mulmod(xx, xx, n) 201 | xx := mulmod(xx, xx, n) 202 | xx := mulmod(mulmod(xx, xx, n), x, n) 203 | xx := mulmod(mulmod(xx, xx, n), x, n) 204 | xx := mulmod(xx, xx, n) 205 | xx := mulmod(mulmod(xx, xx, n), x, n) 206 | xx := mulmod(xx, xx, n) 207 | xx := mulmod(xx, xx, n) 208 | xx := mulmod(mulmod(xx, xx, n), x, n) 209 | xx := mulmod(mulmod(xx, xx, n), x, n) 210 | xx := mulmod(mulmod(xx, xx, n), x, n) 211 | xx := mulmod(mulmod(xx, xx, n), x, n) 212 | xx := mulmod(xx, xx, n) 213 | xx := mulmod(xx, xx, n) 214 | xx := mulmod(xx, xx, n) 215 | xx := mulmod(xx, xx, n) 216 | xx := mulmod(mulmod(xx, xx, n), x, n) 217 | xx := mulmod(xx, xx, n) 218 | xx := mulmod(xx, xx, n) 219 | xx := mulmod(xx, xx, n) 220 | xx := mulmod(xx, xx, n) 221 | xx := mulmod(xx, xx, n) 222 | xx := mulmod(mulmod(xx, xx, n), x, n) 223 | xx := mulmod(xx, xx, n) 224 | xx := mulmod(xx, xx, n) 225 | xx := mulmod(xx, xx, n) 226 | xx := mulmod(mulmod(xx, xx, n), x, n) 227 | xx := mulmod(mulmod(xx, xx, n), x, n) 228 | xx := mulmod(xx, xx, n) 229 | xx := mulmod(xx, xx, n) 230 | xx := mulmod(xx, xx, n) 231 | xx := mulmod(xx, xx, n) 232 | xx := mulmod(xx, xx, n) 233 | xx := mulmod(mulmod(xx, xx, n), x, n) 234 | xx := mulmod(xx, xx, n) 235 | xx := mulmod(mulmod(xx, xx, n), x, n) 236 | xx := mulmod(mulmod(xx, xx, n), x, n) 237 | xx := mulmod(xx, xx, n) 238 | xx := mulmod(mulmod(xx, xx, n), x, n) 239 | xx := mulmod(mulmod(xx, xx, n), x, n) 240 | xx := mulmod(xx, xx, n) 241 | xx := mulmod(mulmod(xx, xx, n), x, n) 242 | xx := mulmod(mulmod(xx, xx, n), x, n) 243 | xx := mulmod(xx, xx, n) 244 | xx := mulmod(xx, xx, n) 245 | xx := mulmod(xx, xx, n) 246 | xx := mulmod(xx, xx, n) 247 | xx := mulmod(mulmod(xx, xx, n), x, n) 248 | xx := mulmod(mulmod(xx, xx, n), x, n) 249 | xx := mulmod(mulmod(xx, xx, n), x, n) 250 | xx := mulmod(mulmod(xx, xx, n), x, n) 251 | xx := mulmod(mulmod(xx, xx, n), x, n) 252 | xx := mulmod(xx, xx, n) 253 | xx := mulmod(xx, xx, n) 254 | xx := mulmod(mulmod(xx, xx, n), x, n) 255 | xx := mulmod(mulmod(xx, xx, n), x, n) 256 | xx := mulmod(mulmod(xx, xx, n), x, n) 257 | xx := mulmod(mulmod(xx, xx, n), x, n) 258 | xx := mulmod(mulmod(xx, xx, n), x, n) 259 | xx := mulmod(mulmod(xx, xx, n), x, n) 260 | xx := mulmod(xx, xx, n) 261 | xx := mulmod(mulmod(xx, xx, n), x, n) 262 | xx := mulmod(xx, xx, n) 263 | xx := mulmod(mulmod(xx, xx, n), x, n) 264 | xx := mulmod(xx, xx, n) 265 | xx := mulmod(xx, xx, n) 266 | xx := mulmod(mulmod(xx, xx, n), x, n) 267 | xx := mulmod(xx, xx, n) 268 | } 269 | } 270 | } 271 | -------------------------------------------------------------------------------- /contracts/TestModExp.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.6.8; 3 | 4 | import { ModExp } from "./ModExp.sol"; 5 | 6 | contract TestModExp { 7 | function baseline(uint256 x) public view returns (uint256 xx, uint256 gasCost) { 8 | gasCost = gasleft(); 9 | xx = ModExp.baseline(x); 10 | gasCost = gasCost - gasleft(); 11 | } 12 | 13 | function modexp(uint256 x) public view returns (uint256 xx, uint256 gasCost) { 14 | gasCost = gasleft(); 15 | xx = ModExp.modexp(x); 16 | gasCost = gasCost - gasleft(); 17 | } 18 | 19 | function modexp2(uint256 x) public view returns (uint256 xx, uint256 gasCost) { 20 | gasCost = gasleft(); 21 | xx = ModExp.modexp2(x); 22 | gasCost = gasCost - gasleft(); 23 | } 24 | 25 | function modexp3(uint256 x) public view returns (uint256 xx, uint256 gasCost) { 26 | gasCost = gasleft(); 27 | xx = ModExp.modexp3(x); 28 | gasCost = gasCost - gasleft(); 29 | } 30 | 31 | function modexp4(uint256 x) public view returns (uint256 xx, uint256 gasCost) { 32 | gasCost = gasleft(); 33 | xx = ModExp.modexp4(x); 34 | gasCost = gasCost - gasleft(); 35 | } 36 | 37 | function modexp5(uint256 x) public view returns (uint256 xx, uint256 gasCost) { 38 | gasCost = gasleft(); 39 | xx = ModExp.modexp5(x); 40 | gasCost = gasCost - gasleft(); 41 | } 42 | 43 | function modexp6(uint256 x) public view returns (uint256 xx, uint256 gasCost) { 44 | gasCost = gasleft(); 45 | xx = ModExp.modexp6(x); 46 | gasCost = gasCost - gasleft(); 47 | } 48 | 49 | function modexp7(uint256 x) public view returns (uint256 xx, uint256 gasCost) { 50 | gasCost = gasleft(); 51 | xx = ModExp.modexp7(x); 52 | gasCost = gasCost - gasleft(); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /magic_codegen.py: -------------------------------------------------------------------------------- 1 | import random 2 | from typing import List, Sequence 3 | 4 | import pytest 5 | from eth_utils import decode_hex 6 | from misc_crypto.utils.assembly import Contract 7 | from web3 import EthereumTesterProvider, Web3 8 | 9 | n = 0x30644E72E131A029B85045B68181585D97816A916871CA8D3C208C16D87CFD47 10 | 11 | 12 | def to_32bytes(x: int): 13 | return x.to_bytes(32, "big") 14 | 15 | 16 | def get_function_selector(function_signature: str) -> str: 17 | return Web3.keccak(function_signature.encode()).hex()[:10] 18 | 19 | 20 | def pymodexp(x: int) -> int: 21 | return pow(x, (n + 1) // 4, n) 22 | 23 | 24 | class ImprovedContract(Contract): 25 | prev_stack_vars: List[str] 26 | 27 | def init_stack_vars(self, init_stack_vars: Sequence[str]) -> None: 28 | self.prev_stack_vars = [] 29 | for var in init_stack_vars: 30 | self.prev_stack_vars.append(var) 31 | 32 | # adapted from poseidon contract helper func 33 | def check_selector(self, signature4bytes: str, dest_label: str) -> None: 34 | """ 35 | Check if the evm message selects the correct function's 4 bytes signature 36 | """ 37 | ( 38 | self.push(b"\x01" + b"\x00" * 28) 39 | .push(0) 40 | .calldataload() 41 | .div() 42 | .push(signature4bytes) 43 | .eq() 44 | .jmpi(dest_label) 45 | .invalid() 46 | ) 47 | 48 | def try_dup(self, var: str): 49 | distance = list(reversed(self.prev_stack_vars)).index(var) 50 | if distance > 15: 51 | raise Exception( 52 | "unlucky bit pattern, no 'n' within range to DUP. Need to mload, unhandled" 53 | ) 54 | # add 'n', by duplicating last 'n' value 55 | self.dup(distance + 1) 56 | self.prev_stack_vars.append(var) 57 | 58 | @property 59 | def stack_size(self): 60 | return len(self.prev_stack_vars) 61 | 62 | 63 | def code_gen_stack_magic(return_gas=False): 64 | # VERY experimental, but passing some tests. Gas-golfed by @protolambda. 65 | # Thanks to @ChihChengLiang for providing the contract bytecode builder util, and a good start (Monster.sol is 7133 gas) 66 | # Needs some work to utilize in solidity. DUP opcodes are great, but a problem to embed 67 | 68 | # TLDR: 69 | # - push n and x on stack to get started 70 | # - lots of DUPN operations, preparing a stack of 472 copies (within max stack size, I think), ordered exactly to repeatedly call mulmod on. 71 | # Since there are only two cases: (xx x n) and (xx xx n), with trailing stack items that can be prepared. 72 | # - keep running mulmod until the stack is back to normal. Sometimes you need to dup the previous result (the xx xx n argument case) 73 | # - 472*3 (dup cost) + 362*8 (mulmod cost) = 5062 gas. Plus a little for start (put x and n on stack) costs. 74 | 75 | # solidity assembly docs: 76 | # mulmod(x, y, m) F (x * y) % m with arbitrary precision arithmetic 77 | # yellow paper: 78 | # MULMOD = (s_0 * s_1) % s_2 79 | 80 | # case I: mulmod(xx, xx, n) 81 | # setup: n 82 | # *prev instructions* 83 | # pre: n xx 84 | # dup1 85 | # in: n xx xx 86 | # mulmod 87 | # out: xx 88 | 89 | # case II: mulmod(xx, x, n) 90 | # setup: n x 91 | # *prev instructions* 92 | # pre: n x xx 93 | # in: n x xx 94 | # mulmod 95 | # out: xx 96 | 97 | contract = ImprovedContract() 98 | 99 | contract.check_selector(get_function_selector("modexp(uint256)"), "start") 100 | 101 | contract.label("start") 102 | 103 | if return_gas: 104 | contract.gas() # stack: 105 | 106 | # N = 0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47 107 | # (N + 1) / 4 = 0xc19139cb84c680a6e14116da060561765e05aa45a1c72a34f082305b61f3f52 108 | bits = bin(0xC19139CB84C680A6E14116DA060561765E05AA45A1C72A34F082305B61F3F52)[2:] 109 | 110 | # load x 111 | # [Selector (4)] [data1 (32)] 112 | contract.push(0x04).calldataload() 113 | 114 | # load n 115 | contract.push(str(hex(n))) 116 | 117 | # stack filling preparation 118 | contract.init_stack_vars(["x", "n"]) 119 | 120 | # prepare stack 121 | # The last preparations get consumed first, so iterate the bits in reverse order. 122 | for original_index, bit_value in reversed(list(enumerate(bits))): 123 | 124 | # what is being prepared (works like the "monster" code): 125 | # if i == "0": 126 | # print("xx:= mulmod(xx, xx, n)") 127 | # else: 128 | # print("xx:= mulmod(mulmod(xx, xx, n), x, n)") 129 | # 130 | 131 | # reverse order: prepare mulmod(result, x, n) before preparing mulmod(xx, xx, n) 132 | # the last thing gets consumed first. 133 | if bit_value == "1": 134 | # prepare case II: push 'n' to the stack, then push 'x' to the stack 135 | contract.try_dup("n") 136 | contract.try_dup("x") 137 | 138 | # prepare case I: push 'n' to the stack 139 | contract.try_dup("n") 140 | 141 | print(f"prepared stack size: {contract.stack_size}") 142 | 143 | # done preparing stack, now write the mulmod and dup operations to interpret this all 144 | # add initial xx value to the stack 145 | contract.push(to_32bytes(1)) 146 | 147 | # work through stack next: 148 | for i, v in enumerate(bits): 149 | # stack is prepared, just need to run the calculations. 150 | # stack: ....... n xx 151 | contract.dup(1) # stack: ....... n xx xx 152 | contract.mulmod() # stack: ....... xx' 153 | if v == "1": 154 | # stack: ... n x xx' 155 | contract.mulmod() # stack: ... xx'' 156 | 157 | # stack: x n xx 158 | 159 | # done working through stack 160 | # get stack back to normal, with result in stack 0 161 | contract.swap(2) # stack: xx n x 162 | 163 | contract.pop().pop() # stack: xx 164 | 165 | if return_gas: 166 | # stack is actually: xx 167 | contract.gas() # stack: 168 | contract.dup(3) # stack: 169 | contract.sub() # stack: 170 | contract.swap(2).pop() # stack: 171 | 172 | contract.push(0) # stack: 0 173 | contract.mstore() # stack: . Memory[0]: 174 | 175 | contract.push(0x20) # stack: 32 176 | contract.mstore() # stack: (empty). Memory[0]: , Memory[1]: 177 | 178 | # Return 64 bytes from memory 179 | contract.push(0x40) 180 | contract.push(0x00) 181 | contract.return_() 182 | else: 183 | contract.push(0) # stack: xx 0 184 | contract.mstore() # stack: (empty). Memory[0]: xx 185 | # Return 32 bytes from memory 186 | contract.push(0x20) 187 | contract.push(0x00) 188 | contract.return_() 189 | 190 | return contract 191 | 192 | 193 | ABI = [ 194 | { 195 | "constant": True, 196 | "inputs": [{"name": "input", "type": "uint256"}], 197 | "name": "modexp", 198 | "outputs": [{"name": "", "type": "uint256"}], 199 | "payable": False, 200 | "stateMutability": "pure", 201 | "type": "function", 202 | } 203 | ] 204 | 205 | ABI_RETURN_GAS = [ 206 | { 207 | "constant": True, 208 | "inputs": [{"name": "input", "type": "uint256"}], 209 | "name": "modexp", 210 | "outputs": [ 211 | {"name": "xx", "type": "uint256"}, 212 | {"name": "gas", "type": "uint256"}, 213 | ], 214 | "payable": False, 215 | "stateMutability": "pure", 216 | "type": "function", 217 | } 218 | ] 219 | 220 | 221 | TEST_COUNT = 30 222 | 223 | 224 | @pytest.mark.parametrize("debug_gas", (False, True)) 225 | def test_build_contract(debug_gas): 226 | contract = code_gen_stack_magic(debug_gas) 227 | abi = ABI_RETURN_GAS if debug_gas else ABI 228 | 229 | w3 = Web3(EthereumTesterProvider()) 230 | contract = w3.eth.contract(abi=abi, bytecode=decode_hex(contract.create_tx_data())) 231 | tx_hash = contract.constructor().transact() 232 | tx_receipt = w3.eth.waitForTransactionReceipt(tx_hash) 233 | instance = w3.eth.contract(address=tx_receipt.contractAddress, abi=abi) 234 | 235 | gases = [] 236 | 237 | def contract_modexp(x: int) -> int: 238 | if debug_gas: 239 | out, gas = instance.functions.modexp(x).call() 240 | gases.append(gas) 241 | else: 242 | out = instance.functions.modexp(x).call() 243 | return out 244 | 245 | assert ( 246 | contract_modexp(3) == 4407920970296243842837207485651524041948558517760411303933 247 | ) 248 | 249 | rng = random.Random(123) 250 | random_cases = [rng.randrange(0, 2 ** 256) for _ in range(TEST_COUNT)] 251 | 252 | for x in random_cases: 253 | assert contract_modexp(x) == pymodexp(x) 254 | if debug_gas: 255 | print("Average gas", sum(gases) / len(gases)) 256 | -------------------------------------------------------------------------------- /modexp.py: -------------------------------------------------------------------------------- 1 | n = 0x30644E72E131A029B85045B68181585D97816A916871CA8D3C208C16D87CFD47 2 | 3 | 4 | def modexp(x): 5 | """ 6 | >>> modexp(3) 7 | 4407920970296243842837207485651524041948558517760411303933 8 | """ 9 | return pow(x, (n + 1) // 4, n) 10 | 11 | 12 | def code_gen(): 13 | for i in bin(0xC19139CB84C680A6E14116DA060561765E05AA45A1C72A34F082305B61F3F52)[2:]: 14 | if i == "0": 15 | print("xx:= mulmod(xx, xx, n)") 16 | else: 17 | print("xx:= mulmod(mulmod(xx, xx, n), x, n)") 18 | 19 | 20 | code_gen() 21 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "modexp", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "buidler test", 8 | "prettier": "prettier . --write" 9 | }, 10 | "author": "", 11 | "license": "ISC", 12 | "devDependencies": { 13 | "@nomiclabs/buidler": "^1.4.4", 14 | "@nomiclabs/buidler-ethers": "^2.0.0", 15 | "@nomiclabs/buidler-waffle": "^2.0.0", 16 | "@types/chai": "^4.2.12", 17 | "@types/mocha": "^8.0.3", 18 | "@types/node": "^14.6.1", 19 | "bn.js": "^5.1.3", 20 | "chai": "^4.2.0", 21 | "ethereum-waffle": "^3.0.2", 22 | "ethers": "^5.0.9", 23 | "prettier": "^2.1.1", 24 | "prettier-plugin-solidity": "^1.0.0-alpha.56", 25 | "ts-node": "^9.0.0", 26 | "typescript": "^4.0.2" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /reprice.py: -------------------------------------------------------------------------------- 1 | """ 2 | https://github.com/ethereum/EIPs/blob/master/EIPS/eip-198.md 3 | https://eips.ethereum.org/EIPS/eip-2565 4 | https://eips.ethereum.org/EIPS/eip-2046 5 | """ 6 | 7 | import math 8 | 9 | GQUADDIVISOR_198 = 20 10 | GQUADDIVISOR_2565_OPTION2 = 3 11 | STATICCALL_BEFORE_2046 = 700 12 | STATICCALL_AFTER_2046 = 40 13 | 14 | n = 0x30644E72E131A029B85045B68181585D97816A916871CA8D3C208C16D87CFD47 15 | 16 | 17 | def mult_complexity_198(x): 18 | if x <= 64: 19 | return x ** 2 20 | elif x <= 1024: 21 | return x ** 2 // 4 + 96 * x - 3072 22 | else: 23 | return x ** 2 // 16 + 480 * x - 199680 24 | 25 | 26 | def mult_complexity_2565(x): 27 | return math.ceil(x / 64) ^ 2 28 | 29 | 30 | gas_cost_now = ( 31 | math.floor(mult_complexity_198(max(32, 32))) 32 | * ((n + 1) // 4).bit_length() 33 | / GQUADDIVISOR_198 34 | + STATICCALL_BEFORE_2046 35 | ) 36 | 37 | gas_cost_2565_option1_and_2046 = ( 38 | max(100, math.floor(mult_complexity_2565(max(32, 32)))) 39 | * ((n + 1) // 4).bit_length() 40 | / GQUADDIVISOR_198 41 | + STATICCALL_AFTER_2046 42 | ) 43 | 44 | gas_cost_2565_option2_and_2046 = ( 45 | max(100, mult_complexity_2565(max(32, 32))) 46 | * ((n + 1) // 4).bit_length() 47 | / GQUADDIVISOR_2565_OPTION2 48 | + STATICCALL_AFTER_2046 49 | ) 50 | 51 | print("Precompile cost now:", gas_cost_now) 52 | print("Precompile cost 2565-option-1 and 2046:", gas_cost_2565_option1_and_2046) 53 | print("Precompile cost 2565-option-2 and 2046:", gas_cost_2565_option2_and_2046) 54 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | git+https://github.com/ChihChengLiang/misc_crypto.git@master 2 | web3[tester] 3 | pytest 4 | -------------------------------------------------------------------------------- /test/modexp.test.ts: -------------------------------------------------------------------------------- 1 | import { ethers } from "@nomiclabs/buidler"; 2 | import { assert } from "chai"; 3 | import { Contract, Signer } from "ethers"; 4 | import BN from "bn.js"; 5 | 6 | const MODEXP_3 = "4407920970296243842837207485651524041948558517760411303933"; 7 | const n = new BN("30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47", 16); 8 | const v = new BN("c19139cb84c680a6e14116da060561765e05aa45a1c72a34f082305b61f3f52", 16); 9 | 10 | function modexp(x: Uint8Array) { 11 | const hex_no0x = ethers.utils.hexlify(x).slice(2); 12 | const x_bn = new BN(hex_no0x, 16); 13 | const reductionContext = BN.red(n); 14 | return x_bn.toRed(reductionContext).redPow(v).fromRed().toString(10); 15 | } 16 | 17 | function reportCost(costs: number[]) { 18 | const avg: number = 19 | costs.reduce((a: number, b: number) => { 20 | return a + b; 21 | }, 0) / costs.length; 22 | console.log("Avg cost", avg); 23 | } 24 | 25 | describe("Ts implementation", function () { 26 | it("works at some inputs", function () { 27 | assert.equal(modexp(ethers.utils.arrayify(3)), MODEXP_3); 28 | }); 29 | }); 30 | 31 | describe("ModExp", function () { 32 | let accounts: Signer[]; 33 | let contract: Contract; 34 | let randomBytes: Uint8Array[] = []; 35 | 36 | before(async function () { 37 | accounts = await ethers.getSigners(); 38 | const factory = await ethers.getContractFactory("TestModExp", accounts[0]); 39 | contract = await factory.deploy(); 40 | const receipt = await contract.deployTransaction.wait(); 41 | console.log("Deployment gas", receipt.gasUsed.toString()); 42 | for (let i = 0; i < 16; i++) { 43 | randomBytes.push(ethers.utils.randomBytes(32)); 44 | } 45 | }); 46 | it("baseline: Calling EIP-198 precompile", async function () { 47 | const [result] = await contract.baseline(3); 48 | assert.equal(result, MODEXP_3); 49 | const costs: number[] = []; 50 | for (const input of randomBytes) { 51 | const [output, cost] = await contract.baseline(input); 52 | assert.equal(output, modexp(input), "Mismatch output"); 53 | costs.push(Number(cost)); 54 | } 55 | reportCost(costs); 56 | }); 57 | 58 | it("modexp 1: Naive Solidity", async function () { 59 | const [result] = await contract.modexp(3); 60 | assert.equal(result, MODEXP_3); 61 | const costs: number[] = []; 62 | for (const input of randomBytes) { 63 | const [output, cost] = await contract.modexp(input); 64 | assert.equal(output, modexp(input), "Mismatch output"); 65 | costs.push(Number(cost)); 66 | } 67 | reportCost(costs); 68 | }); 69 | 70 | it("modexp 2: Naive inline assembly", async function () { 71 | const [result] = await contract.modexp2(3); 72 | assert.equal(result, MODEXP_3); 73 | const costs: number[] = []; 74 | for (const input of randomBytes) { 75 | const [output, cost] = await contract.modexp2(input); 76 | assert.equal(output, modexp(input), "Mismatch output"); 77 | costs.push(Number(cost)); 78 | } 79 | reportCost(costs); 80 | }); 81 | 82 | it("modexp 3: Loop unroll", async function () { 83 | const [result] = await contract.modexp3(3); 84 | assert.equal(result, MODEXP_3); 85 | const costs: number[] = []; 86 | for (const input of randomBytes) { 87 | const [output, cost] = await contract.modexp3(input); 88 | assert.equal(output, modexp(input), "Mismatch output"); 89 | costs.push(Number(cost)); 90 | } 91 | reportCost(costs); 92 | }); 93 | 94 | it("modexp 4: Minor optimize", async function () { 95 | const [result] = await contract.modexp4(3); 96 | assert.equal(result, MODEXP_3); 97 | const costs: number[] = []; 98 | for (const input of randomBytes) { 99 | const [output, cost] = await contract.modexp4(input); 100 | assert.equal(output, modexp(input), "Mismatch output"); 101 | costs.push(Number(cost)); 102 | } 103 | reportCost(costs); 104 | }); 105 | 106 | it("modexp 5: Unroll more", async function () { 107 | const [result] = await contract.modexp5(3); 108 | assert.equal(result, MODEXP_3); 109 | const costs: number[] = []; 110 | for (const input of randomBytes) { 111 | const [output, cost] = await contract.modexp5(input); 112 | assert.equal(output, modexp(input), "Mismatch output"); 113 | costs.push(Number(cost)); 114 | } 115 | reportCost(costs); 116 | }); 117 | 118 | it("modexp 6: Remove if statement", async function () { 119 | const [result] = await contract.modexp6(3); 120 | assert.equal(result, MODEXP_3); 121 | const costs: number[] = []; 122 | for (const input of randomBytes) { 123 | const [output, cost] = await contract.modexp6(input); 124 | assert.equal(output, modexp(input), "Mismatch output"); 125 | costs.push(Number(cost)); 126 | } 127 | reportCost(costs); 128 | }); 129 | 130 | it("modexp 7: Reproduce historical result", async function () { 131 | const [result] = await contract.modexp7(3); 132 | assert.equal(result, MODEXP_3); 133 | const costs: number[] = []; 134 | for (const input of randomBytes) { 135 | const [output, cost] = await contract.modexp7(input); 136 | assert.equal(output, modexp(input), "Mismatch output"); 137 | costs.push(Number(cost)); 138 | } 139 | reportCost(costs); 140 | }); 141 | }); 142 | 143 | describe("ModExp Monster code", function () { 144 | it("modexp 8: monster code", async function () { 145 | const accounts = await ethers.getSigners(); 146 | const factory = await ethers.getContractFactory("Monster", accounts[0]); 147 | const contract = await factory.deploy(); 148 | const receipt = await contract.deployTransaction.wait(); 149 | console.log("Deployment gas", receipt.gasUsed.toString()); 150 | 151 | const randomBytes: Uint8Array[] = []; 152 | 153 | for (let i = 0; i < 16; i++) { 154 | randomBytes.push(ethers.utils.randomBytes(32)); 155 | } 156 | 157 | const [result] = await contract.test_modexp(3); 158 | assert.equal(result, MODEXP_3); 159 | const costs: number[] = []; 160 | for (const input of randomBytes) { 161 | const [output, cost] = await contract.test_modexp(input); 162 | assert.equal(output, modexp(input), "Mismatch output"); 163 | costs.push(Number(cost)); 164 | } 165 | reportCost(costs); 166 | }); 167 | }); 168 | 169 | describe("ModExp AddChainLong", function () { 170 | it("modexp 9: add chain long", async function () { 171 | const accounts = await ethers.getSigners(); 172 | const factory = await ethers.getContractFactory("AddChainLong", accounts[0]); 173 | const contract = await factory.deploy(); 174 | const receipt = await contract.deployTransaction.wait(); 175 | console.log("Deployment gas", receipt.gasUsed.toString()); 176 | 177 | const randomBytes: Uint8Array[] = []; 178 | 179 | for (let i = 0; i < 16; i++) { 180 | randomBytes.push(ethers.utils.randomBytes(32)); 181 | } 182 | 183 | const [result] = await contract.test_modexp(3); 184 | assert.equal(result, MODEXP_3); 185 | const costs: number[] = []; 186 | for (const input of randomBytes) { 187 | const [output, cost] = await contract.test_modexp(input); 188 | assert.equal(output, modexp(input), "Mismatch output"); 189 | costs.push(Number(cost)); 190 | } 191 | reportCost(costs); 192 | }); 193 | }); 194 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "commonjs", 5 | "strict": true, 6 | "esModuleInterop": true, 7 | "outDir": "dist" 8 | }, 9 | "include": ["./scripts", "./test"], 10 | "files": [ 11 | "./buidler.config.ts", 12 | "./node_modules/@nomiclabs/buidler-ethers/src/type-extensions.d.ts", 13 | "./node_modules/@nomiclabs/buidler-waffle/src/type-extensions.d.ts" 14 | ] 15 | } 16 | --------------------------------------------------------------------------------