├── README.md ├── circuit ├── Nargo.toml ├── contract │ └── circuit │ │ └── plonk_vk.sol ├── src │ └── main.nr └── target │ ├── circuit.json │ └── debug_circuit.json ├── contracts └── CommentVerifier.sol ├── relayer ├── .env.example ├── .gitignore ├── json_abi │ └── CommentVerifier.json ├── package-lock.json ├── package.json └── relayer.js ├── screenshot.png └── webapp ├── .env.example ├── .gitignore ├── app.js ├── index.html ├── json_abi └── CommentVerifier.json ├── package-lock.json ├── package.json └── vite.config.js /README.md: -------------------------------------------------------------------------------- 1 | # ⚠️Not maintained anymore, migrated [here](https://github.com/Turupawn/EcrecoverInclusionProof)⚠️ 2 | 3 | # On-chain Privacy DApp Demo 4 | 5 | This demo showcases all the parts needed to create a privacy preserving DApp with good UX which are: 6 | * A circuit 7 | * A smart contract 8 | * A relayer 9 | * A webapp 10 | 11 | On this example we do a simple anonymous message board but it can be extended for other DApps in other fields such as DeFi, voting & governance, identity, etc.. 12 | 13 | ![Privacy zkDApp screenshot](https://raw.githubusercontent.com/Turupawn/PrivacyDAppDemo/master/screenshot.png) 14 | 15 | ## ⭐Features 16 | 17 | | Feature | Supported | 18 | |----------|------------ | 19 | | Aztec Noir circuit | ✅ | 20 | | Solidity verifier | ✅ | 21 | | ECDSA verification circuit | ✅ | 22 | | EIP712 enabled | ✅ | 23 | | Prover on browser (WASM) | ✅ | 24 | | Ethers.js 6.9 relayer | ✅ | 25 | | MIT license | ✅ | 26 | 27 | ## 🚀How to launch 28 | 29 | ### Step 1. Generate and deploy the Solidity verifier 30 | 31 | Make sure you installed Nargo `0.19.4` as detailed below: 32 | 33 |
34 | On Linux 35 | 36 | ```bash 37 | mkdir -p $HOME/.nargo/bin && \ 38 | curl -o $HOME/.nargo/bin/nargo-x86_64-unknown-linux-gnu.tar.gz -L https://github.com/noir-lang/noir/releases/download/v0.19.4/nargo-x86_64-unknown-linux-gnu.tar.gz && \ 39 | tar -xvf $HOME/.nargo/bin/nargo-x86_64-unknown-linux-gnu.tar.gz -C $HOME/.nargo/bin/ && \ 40 | echo 'export PATH=$PATH:$HOME/.nargo/bin' >> ~/.bashrc && \ 41 | source ~/.bashrc 42 | ``` 43 |
44 | 45 |
46 | On MAC 47 | 48 | ```bash 49 | mkdir -p $HOME/.nargo/bin && \ 50 | curl -o $HOME/.nargo/bin/nargo-x86_64-apple-darwin.tar.gz -L https://github.com/noir-lang/noir/releases/download/v0.19.4/nargo-x86_64-apple-darwin.tar.gz && \ 51 | tar -xvf $HOME/.nargo/bin/nargo-x86_64-apple-darwin.tar.gz -C $HOME/.nargo/bin/ && \ 52 | echo '\nexport PATH=$PATH:$HOME/.nargo/bin' >> ~/.zshrc && \ 53 | source ~/.zshrc 54 | ``` 55 |
56 | 57 | Now generate the Solidity verifier. 58 | 59 | ```bash 60 | cd circuit 61 | nargo codegen-verifier 62 | ``` 63 | 64 | This will generate a Solidity file located at `circuit/contract/circuit/plonk_vk.sol`. Deploy it on an EVM on-chain. 65 | 66 | ### Step 2. Deploy the verifier contract 67 | 68 | Now deploy the `CommentVerifier` contract located at `contracts/CommentVerifier.sol`. Pass the Verifier contract you just generated as constructor parameter. 69 | 70 | ### Step 3. Launch the Relayer 71 | 72 | Let's launch the relayer first. Fill the `.env` file based on `.env.example` on the `relayer/` directory and run the following. 73 | 74 | ```bash 75 | cd relayer 76 | npm install 77 | npm start 78 | ``` 79 | 80 | ### Setp 4. Launch the webapp and verify a proof 81 | 82 | Open a new terminal and launch the webapp. Now fill the `.env` file based on `.env.example` on the `webapp/`, the run the following. 83 | 84 | ```bash 85 | cd webapp 86 | npm install 87 | npm start 88 | ``` 89 | 90 | The webapp will automatically open on your browser. Now you will be able to generate proofs on your browser and send them to the relayer for on-chain verification. 91 | 92 | ## ⚠️Known issues (PRs welcome) 93 | 94 | * We need to compress the hashed message params to reduce L1 fees on L2s. We should use [this](https://github.com/Bank-of-JubJub/base/blob/2a0247a441463a6619cc8d5f13d81717d166b770/hardhat/contracts/UsingAccountControllers.sol#L158) and [this](https://github.com/Bank-of-JubJub/base/blob/master/circuits/change_eth_signer/src/main.nr) 95 | -------------------------------------------------------------------------------- /circuit/Nargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "circuit" 3 | type = "bin" 4 | authors = [""] 5 | compiler_version = "0.19.4" 6 | 7 | [dependencies] -------------------------------------------------------------------------------- /circuit/contract/circuit/plonk_vk.sol: -------------------------------------------------------------------------------- 1 | // Verification Key Hash: 457c7abc3bffd9c93470754f3ad9e49570fcd18b745ded4cc82d15acd36ba10f 2 | // SPDX-License-Identifier: Apache-2.0 3 | // Copyright 2022 Aztec 4 | pragma solidity >=0.8.4; 5 | 6 | library UltraVerificationKey { 7 | function verificationKeyHash() internal pure returns(bytes32) { 8 | return 0x457c7abc3bffd9c93470754f3ad9e49570fcd18b745ded4cc82d15acd36ba10f; 9 | } 10 | 11 | function loadVerificationKey(uint256 _vk, uint256 _omegaInverseLoc) internal pure { 12 | assembly { 13 | mstore(add(_vk, 0x00), 0x0000000000000000000000000000000000000000000000000000000000010000) // vk.circuit_size 14 | mstore(add(_vk, 0x20), 0x0000000000000000000000000000000000000000000000000000000000000020) // vk.num_inputs 15 | mstore(add(_vk, 0x40), 0x00eeb2cb5981ed45649abebde081dcff16c8601de4347e7dd1628ba2daac43b7) // vk.work_root 16 | mstore(add(_vk, 0x60), 0x30641e0e92bebef818268d663bcad6dbcfd6c0149170f6d7d350b1b1fa6c1001) // vk.domain_inverse 17 | mstore(add(_vk, 0x80), 0x108d4f3cdfafd42ec771f87cd087eee2c48cf817a945e0e97af440c4931750cf) // vk.Q1.x 18 | mstore(add(_vk, 0xa0), 0x0e20c16ebcf4d9b67fa256442cf7be89d014ec70f5987b22a9ea96253cf8a919) // vk.Q1.y 19 | mstore(add(_vk, 0xc0), 0x25d896eae69ba90acc3de581b20ca089a08f2fa0bb9a4ee2a1138e2d72534ab7) // vk.Q2.x 20 | mstore(add(_vk, 0xe0), 0x17f781d2b70ac428782716a0e1c1383bbe80cee8a89d26371d766ba7b915adb9) // vk.Q2.y 21 | mstore(add(_vk, 0x100), 0x20b7896d57e7a4ee3f34d7b435d5e2e2f710b0f8b8ebf9393ce39eded1e10c37) // vk.Q3.x 22 | mstore(add(_vk, 0x120), 0x285b48100ee0187be624e861f1ff7a2c98b649f7ad6ef9ced99192b9b6d0632e) // vk.Q3.y 23 | mstore(add(_vk, 0x140), 0x21d0968189c2449cfb641fc32810af036146156ce79674b105d399973fd3e19b) // vk.Q4.x 24 | mstore(add(_vk, 0x160), 0x24a75c0f6ed1ef3dc7e6fce31809d1c9824b515f6fb9ddcbf0a9991667d313bd) // vk.Q4.y 25 | mstore(add(_vk, 0x180), 0x23fcfe2c41069bdba6019609571bcc029261e67e2b7e7d53f4e7fd6869c3e9f1) // vk.Q_M.x 26 | mstore(add(_vk, 0x1a0), 0x1a4b515b855b38067ca2dec6528088c46ded7538d524c8fd7f2baab1cabca74a) // vk.Q_M.y 27 | mstore(add(_vk, 0x1c0), 0x205c12bdb88ed3205a0723e1adfc91ceaafac7d0d3c176dc959788fa9b6e9745) // vk.Q_C.x 28 | mstore(add(_vk, 0x1e0), 0x02dfe447d0c6653218054d0d4d0c2252fb567672975ae76a8dbc971959ef61d3) // vk.Q_C.y 29 | mstore(add(_vk, 0x200), 0x2a03ec59509fa399be2c1b9e24b56a1a3e63ae6aa66640349c4852cad93e06ee) // vk.Q_ARITHMETIC.x 30 | mstore(add(_vk, 0x220), 0x026fbbb98e200421811825cccef4b1384ad004a349035a0b1ffc088ba54f12e6) // vk.Q_ARITHMETIC.y 31 | mstore(add(_vk, 0x240), 0x060f5ed4ff5ee5d867e6a328fa5bbb97617202677923e3c93c3a1f62158495df) // vk.QSORT.x 32 | mstore(add(_vk, 0x260), 0x06908e96cb3f7e7796d970ab0f2f25234540658291cc53e16831c12cbdad5195) // vk.QSORT.y 33 | mstore(add(_vk, 0x280), 0x21245d6c0a4d2ff12b21a825f39f30e8f8cf9b259448d111183e975828539576) // vk.Q_ELLIPTIC.x 34 | mstore(add(_vk, 0x2a0), 0x16a409532c8a1693536e93b6ce9920bfc2e6796e8dfe404675a0cdf6ee77ee7a) // vk.Q_ELLIPTIC.y 35 | mstore(add(_vk, 0x2c0), 0x18e60c34b949ff280b9235cc41eaccb233ac66bdfa3a2a575788513a26d80b95) // vk.Q_AUX.x 36 | mstore(add(_vk, 0x2e0), 0x22cb3abd7f72f5e97f8234ea41dc793fa8953a104482a832725765582e6848e9) // vk.Q_AUX.y 37 | mstore(add(_vk, 0x300), 0x01788136a6ba924bbdf04ad0c1de1a0351ae089dba8035aa40e670673a84eb64) // vk.SIGMA1.x 38 | mstore(add(_vk, 0x320), 0x06573b2e8088659550771d93a366587edffc7423d4009fdaa3102204b9cb54ec) // vk.SIGMA1.y 39 | mstore(add(_vk, 0x340), 0x14260e2a7ac353f00a01de9ea6e5aa572772ff70dcf77af24afb421b2147188e) // vk.SIGMA2.x 40 | mstore(add(_vk, 0x360), 0x005b4eb7779137fa90c0a45fa14331ac71d167fa674eef01547a2939e0499c43) // vk.SIGMA2.y 41 | mstore(add(_vk, 0x380), 0x019a7ad0b7b511889f2729c51e769be7beff803a632d267403da32327508a137) // vk.SIGMA3.x 42 | mstore(add(_vk, 0x3a0), 0x04f7648d9e8ad203a38c39e268ca1a17714f2cc4d609fdaacb601a936c8a2cba) // vk.SIGMA3.y 43 | mstore(add(_vk, 0x3c0), 0x11b8da2236efce2203b3c96e6cd2436306dd8ef5e8b020ddd9472b023faadab4) // vk.SIGMA4.x 44 | mstore(add(_vk, 0x3e0), 0x2e0bb47a65bd603ba5349c19277b8fc01cd3e82677f510e5667c586ebe07541d) // vk.SIGMA4.y 45 | mstore(add(_vk, 0x400), 0x06e6d1480535f6e29a6713cd5c4e668464cbdac425322b2d3b98a34e8834c14f) // vk.TABLE1.x 46 | mstore(add(_vk, 0x420), 0x15c1b7ea4800a867676fc4c79695b5b27b0e702d7d6188d0ad0259936cb3e55f) // vk.TABLE1.y 47 | mstore(add(_vk, 0x440), 0x030b16cd4d48e7b40d2ac90b515369aabc0c273bd7c77f122e9993c2251f7f4a) // vk.TABLE2.x 48 | mstore(add(_vk, 0x460), 0x07578950db871ef62bab02e94473298e53701113cc0c09573d1ccdbb99ab49b7) // vk.TABLE2.y 49 | mstore(add(_vk, 0x480), 0x1bbfe736b28dc8ede22ad6ba3ecec3607de18a29cee6828a7303d33ceb1e2ef0) // vk.TABLE3.x 50 | mstore(add(_vk, 0x4a0), 0x2e0a348344704012f149db2aed39ea37d1fe96d4d91f803026f17ed9a0449907) // vk.TABLE3.y 51 | mstore(add(_vk, 0x4c0), 0x273714e498d7ee7471cf8859c5db4113e86f6acee195b29fd0e5953728135962) // vk.TABLE4.x 52 | mstore(add(_vk, 0x4e0), 0x2997e7f0a5012a52da8388d912ce58fffbab8f6b5d56b6c289ab367fd72f242a) // vk.TABLE4.y 53 | mstore(add(_vk, 0x500), 0x0778f9f2ccf80ad63301b1dc3c794b38b3cdbee200766df7d35b373acb6da798) // vk.TABLE_TYPE.x 54 | mstore(add(_vk, 0x520), 0x2bd1c34249d7e3745f6818cd46a2954c5df21eff4b9b9739d7c667c72314246b) // vk.TABLE_TYPE.y 55 | mstore(add(_vk, 0x540), 0x26d992dc6a0c2d3dc7577d94ddd4503a96c6abf86cb4264088e930942f1a36e4) // vk.ID1.x 56 | mstore(add(_vk, 0x560), 0x1784262c3e269323fe1a43eaf9c4afa6af67f52f1e083f2f661384d7a186d0f2) // vk.ID1.y 57 | mstore(add(_vk, 0x580), 0x20fe45d0f5ff473164023a86fb0140af519db8ebdc25703035698d9aa4cbe644) // vk.ID2.x 58 | mstore(add(_vk, 0x5a0), 0x14f5c79f909d493994b20289e31737a8ccceee60a6b45ef144ac673aca453677) // vk.ID2.y 59 | mstore(add(_vk, 0x5c0), 0x111760ed0e46b0252d0dc90fd0d24f6b018531c15ad1eb412fae73c1d842515d) // vk.ID3.x 60 | mstore(add(_vk, 0x5e0), 0x2a1f5677c6f6e18d1092ce194239a02635b9385d079dd2bc46c052bf3173651e) // vk.ID3.y 61 | mstore(add(_vk, 0x600), 0x2ba1c34652a40c5c66d8695e388f5bc265fdb89de32649931cebb2b389fc67c7) // vk.ID4.x 62 | mstore(add(_vk, 0x620), 0x1b050a94f377c90873be9448610580d634074191dc96f00d1d804c5097676239) // vk.ID4.y 63 | mstore(add(_vk, 0x640), 0x00) // vk.contains_recursive_proof 64 | mstore(add(_vk, 0x660), 0) // vk.recursive_proof_public_input_indices 65 | mstore(add(_vk, 0x680), 0x260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c1) // vk.g2_x.X.c1 66 | mstore(add(_vk, 0x6a0), 0x0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b0) // vk.g2_x.X.c0 67 | mstore(add(_vk, 0x6c0), 0x04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe4) // vk.g2_x.Y.c1 68 | mstore(add(_vk, 0x6e0), 0x22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e55) // vk.g2_x.Y.c0 69 | mstore(_omegaInverseLoc, 0x0b5d56b77fe704e8e92338c0082f37e091126414c830e4c6922d5ac802d842d4) // vk.work_root_inverse 70 | } 71 | } 72 | } 73 | /** 74 | * @title Ultra Plonk proof verification contract 75 | * @dev Top level Plonk proof verification contract, which allows Plonk proof to be verified 76 | */ 77 | abstract contract BaseUltraVerifier { 78 | // VERIFICATION KEY MEMORY LOCATIONS 79 | uint256 internal constant N_LOC = 0x380; 80 | uint256 internal constant NUM_INPUTS_LOC = 0x3a0; 81 | uint256 internal constant OMEGA_LOC = 0x3c0; 82 | uint256 internal constant DOMAIN_INVERSE_LOC = 0x3e0; 83 | uint256 internal constant Q1_X_LOC = 0x400; 84 | uint256 internal constant Q1_Y_LOC = 0x420; 85 | uint256 internal constant Q2_X_LOC = 0x440; 86 | uint256 internal constant Q2_Y_LOC = 0x460; 87 | uint256 internal constant Q3_X_LOC = 0x480; 88 | uint256 internal constant Q3_Y_LOC = 0x4a0; 89 | uint256 internal constant Q4_X_LOC = 0x4c0; 90 | uint256 internal constant Q4_Y_LOC = 0x4e0; 91 | uint256 internal constant QM_X_LOC = 0x500; 92 | uint256 internal constant QM_Y_LOC = 0x520; 93 | uint256 internal constant QC_X_LOC = 0x540; 94 | uint256 internal constant QC_Y_LOC = 0x560; 95 | uint256 internal constant QARITH_X_LOC = 0x580; 96 | uint256 internal constant QARITH_Y_LOC = 0x5a0; 97 | uint256 internal constant QSORT_X_LOC = 0x5c0; 98 | uint256 internal constant QSORT_Y_LOC = 0x5e0; 99 | uint256 internal constant QELLIPTIC_X_LOC = 0x600; 100 | uint256 internal constant QELLIPTIC_Y_LOC = 0x620; 101 | uint256 internal constant QAUX_X_LOC = 0x640; 102 | uint256 internal constant QAUX_Y_LOC = 0x660; 103 | uint256 internal constant SIGMA1_X_LOC = 0x680; 104 | uint256 internal constant SIGMA1_Y_LOC = 0x6a0; 105 | uint256 internal constant SIGMA2_X_LOC = 0x6c0; 106 | uint256 internal constant SIGMA2_Y_LOC = 0x6e0; 107 | uint256 internal constant SIGMA3_X_LOC = 0x700; 108 | uint256 internal constant SIGMA3_Y_LOC = 0x720; 109 | uint256 internal constant SIGMA4_X_LOC = 0x740; 110 | uint256 internal constant SIGMA4_Y_LOC = 0x760; 111 | uint256 internal constant TABLE1_X_LOC = 0x780; 112 | uint256 internal constant TABLE1_Y_LOC = 0x7a0; 113 | uint256 internal constant TABLE2_X_LOC = 0x7c0; 114 | uint256 internal constant TABLE2_Y_LOC = 0x7e0; 115 | uint256 internal constant TABLE3_X_LOC = 0x800; 116 | uint256 internal constant TABLE3_Y_LOC = 0x820; 117 | uint256 internal constant TABLE4_X_LOC = 0x840; 118 | uint256 internal constant TABLE4_Y_LOC = 0x860; 119 | uint256 internal constant TABLE_TYPE_X_LOC = 0x880; 120 | uint256 internal constant TABLE_TYPE_Y_LOC = 0x8a0; 121 | uint256 internal constant ID1_X_LOC = 0x8c0; 122 | uint256 internal constant ID1_Y_LOC = 0x8e0; 123 | uint256 internal constant ID2_X_LOC = 0x900; 124 | uint256 internal constant ID2_Y_LOC = 0x920; 125 | uint256 internal constant ID3_X_LOC = 0x940; 126 | uint256 internal constant ID3_Y_LOC = 0x960; 127 | uint256 internal constant ID4_X_LOC = 0x980; 128 | uint256 internal constant ID4_Y_LOC = 0x9a0; 129 | uint256 internal constant CONTAINS_RECURSIVE_PROOF_LOC = 0x9c0; 130 | uint256 internal constant RECURSIVE_PROOF_PUBLIC_INPUT_INDICES_LOC = 0x9e0; 131 | uint256 internal constant G2X_X0_LOC = 0xa00; 132 | uint256 internal constant G2X_X1_LOC = 0xa20; 133 | uint256 internal constant G2X_Y0_LOC = 0xa40; 134 | uint256 internal constant G2X_Y1_LOC = 0xa60; 135 | 136 | // ### PROOF DATA MEMORY LOCATIONS 137 | uint256 internal constant W1_X_LOC = 0x1200; 138 | uint256 internal constant W1_Y_LOC = 0x1220; 139 | uint256 internal constant W2_X_LOC = 0x1240; 140 | uint256 internal constant W2_Y_LOC = 0x1260; 141 | uint256 internal constant W3_X_LOC = 0x1280; 142 | uint256 internal constant W3_Y_LOC = 0x12a0; 143 | uint256 internal constant W4_X_LOC = 0x12c0; 144 | uint256 internal constant W4_Y_LOC = 0x12e0; 145 | uint256 internal constant S_X_LOC = 0x1300; 146 | uint256 internal constant S_Y_LOC = 0x1320; 147 | uint256 internal constant Z_X_LOC = 0x1340; 148 | uint256 internal constant Z_Y_LOC = 0x1360; 149 | uint256 internal constant Z_LOOKUP_X_LOC = 0x1380; 150 | uint256 internal constant Z_LOOKUP_Y_LOC = 0x13a0; 151 | uint256 internal constant T1_X_LOC = 0x13c0; 152 | uint256 internal constant T1_Y_LOC = 0x13e0; 153 | uint256 internal constant T2_X_LOC = 0x1400; 154 | uint256 internal constant T2_Y_LOC = 0x1420; 155 | uint256 internal constant T3_X_LOC = 0x1440; 156 | uint256 internal constant T3_Y_LOC = 0x1460; 157 | uint256 internal constant T4_X_LOC = 0x1480; 158 | uint256 internal constant T4_Y_LOC = 0x14a0; 159 | 160 | uint256 internal constant W1_EVAL_LOC = 0x1600; 161 | uint256 internal constant W2_EVAL_LOC = 0x1620; 162 | uint256 internal constant W3_EVAL_LOC = 0x1640; 163 | uint256 internal constant W4_EVAL_LOC = 0x1660; 164 | uint256 internal constant S_EVAL_LOC = 0x1680; 165 | uint256 internal constant Z_EVAL_LOC = 0x16a0; 166 | uint256 internal constant Z_LOOKUP_EVAL_LOC = 0x16c0; 167 | uint256 internal constant Q1_EVAL_LOC = 0x16e0; 168 | uint256 internal constant Q2_EVAL_LOC = 0x1700; 169 | uint256 internal constant Q3_EVAL_LOC = 0x1720; 170 | uint256 internal constant Q4_EVAL_LOC = 0x1740; 171 | uint256 internal constant QM_EVAL_LOC = 0x1760; 172 | uint256 internal constant QC_EVAL_LOC = 0x1780; 173 | uint256 internal constant QARITH_EVAL_LOC = 0x17a0; 174 | uint256 internal constant QSORT_EVAL_LOC = 0x17c0; 175 | uint256 internal constant QELLIPTIC_EVAL_LOC = 0x17e0; 176 | uint256 internal constant QAUX_EVAL_LOC = 0x1800; 177 | uint256 internal constant TABLE1_EVAL_LOC = 0x1840; 178 | uint256 internal constant TABLE2_EVAL_LOC = 0x1860; 179 | uint256 internal constant TABLE3_EVAL_LOC = 0x1880; 180 | uint256 internal constant TABLE4_EVAL_LOC = 0x18a0; 181 | uint256 internal constant TABLE_TYPE_EVAL_LOC = 0x18c0; 182 | uint256 internal constant ID1_EVAL_LOC = 0x18e0; 183 | uint256 internal constant ID2_EVAL_LOC = 0x1900; 184 | uint256 internal constant ID3_EVAL_LOC = 0x1920; 185 | uint256 internal constant ID4_EVAL_LOC = 0x1940; 186 | uint256 internal constant SIGMA1_EVAL_LOC = 0x1960; 187 | uint256 internal constant SIGMA2_EVAL_LOC = 0x1980; 188 | uint256 internal constant SIGMA3_EVAL_LOC = 0x19a0; 189 | uint256 internal constant SIGMA4_EVAL_LOC = 0x19c0; 190 | uint256 internal constant W1_OMEGA_EVAL_LOC = 0x19e0; 191 | uint256 internal constant W2_OMEGA_EVAL_LOC = 0x2000; 192 | uint256 internal constant W3_OMEGA_EVAL_LOC = 0x2020; 193 | uint256 internal constant W4_OMEGA_EVAL_LOC = 0x2040; 194 | uint256 internal constant S_OMEGA_EVAL_LOC = 0x2060; 195 | uint256 internal constant Z_OMEGA_EVAL_LOC = 0x2080; 196 | uint256 internal constant Z_LOOKUP_OMEGA_EVAL_LOC = 0x20a0; 197 | uint256 internal constant TABLE1_OMEGA_EVAL_LOC = 0x20c0; 198 | uint256 internal constant TABLE2_OMEGA_EVAL_LOC = 0x20e0; 199 | uint256 internal constant TABLE3_OMEGA_EVAL_LOC = 0x2100; 200 | uint256 internal constant TABLE4_OMEGA_EVAL_LOC = 0x2120; 201 | 202 | uint256 internal constant PI_Z_X_LOC = 0x2300; 203 | uint256 internal constant PI_Z_Y_LOC = 0x2320; 204 | uint256 internal constant PI_Z_OMEGA_X_LOC = 0x2340; 205 | uint256 internal constant PI_Z_OMEGA_Y_LOC = 0x2360; 206 | 207 | // Used for elliptic widget. These are alias names for wire + shifted wire evaluations 208 | uint256 internal constant X1_EVAL_LOC = W2_EVAL_LOC; 209 | uint256 internal constant X2_EVAL_LOC = W1_OMEGA_EVAL_LOC; 210 | uint256 internal constant X3_EVAL_LOC = W2_OMEGA_EVAL_LOC; 211 | uint256 internal constant Y1_EVAL_LOC = W3_EVAL_LOC; 212 | uint256 internal constant Y2_EVAL_LOC = W4_OMEGA_EVAL_LOC; 213 | uint256 internal constant Y3_EVAL_LOC = W3_OMEGA_EVAL_LOC; 214 | uint256 internal constant QBETA_LOC = Q3_EVAL_LOC; 215 | uint256 internal constant QBETA_SQR_LOC = Q4_EVAL_LOC; 216 | uint256 internal constant QSIGN_LOC = Q1_EVAL_LOC; 217 | 218 | // ### CHALLENGES MEMORY OFFSETS 219 | 220 | uint256 internal constant C_BETA_LOC = 0x2600; 221 | uint256 internal constant C_GAMMA_LOC = 0x2620; 222 | uint256 internal constant C_ALPHA_LOC = 0x2640; 223 | uint256 internal constant C_ETA_LOC = 0x2660; 224 | uint256 internal constant C_ETA_SQR_LOC = 0x2680; 225 | uint256 internal constant C_ETA_CUBE_LOC = 0x26a0; 226 | 227 | uint256 internal constant C_ZETA_LOC = 0x26c0; 228 | uint256 internal constant C_CURRENT_LOC = 0x26e0; 229 | uint256 internal constant C_V0_LOC = 0x2700; 230 | uint256 internal constant C_V1_LOC = 0x2720; 231 | uint256 internal constant C_V2_LOC = 0x2740; 232 | uint256 internal constant C_V3_LOC = 0x2760; 233 | uint256 internal constant C_V4_LOC = 0x2780; 234 | uint256 internal constant C_V5_LOC = 0x27a0; 235 | uint256 internal constant C_V6_LOC = 0x27c0; 236 | uint256 internal constant C_V7_LOC = 0x27e0; 237 | uint256 internal constant C_V8_LOC = 0x2800; 238 | uint256 internal constant C_V9_LOC = 0x2820; 239 | uint256 internal constant C_V10_LOC = 0x2840; 240 | uint256 internal constant C_V11_LOC = 0x2860; 241 | uint256 internal constant C_V12_LOC = 0x2880; 242 | uint256 internal constant C_V13_LOC = 0x28a0; 243 | uint256 internal constant C_V14_LOC = 0x28c0; 244 | uint256 internal constant C_V15_LOC = 0x28e0; 245 | uint256 internal constant C_V16_LOC = 0x2900; 246 | uint256 internal constant C_V17_LOC = 0x2920; 247 | uint256 internal constant C_V18_LOC = 0x2940; 248 | uint256 internal constant C_V19_LOC = 0x2960; 249 | uint256 internal constant C_V20_LOC = 0x2980; 250 | uint256 internal constant C_V21_LOC = 0x29a0; 251 | uint256 internal constant C_V22_LOC = 0x29c0; 252 | uint256 internal constant C_V23_LOC = 0x29e0; 253 | uint256 internal constant C_V24_LOC = 0x2a00; 254 | uint256 internal constant C_V25_LOC = 0x2a20; 255 | uint256 internal constant C_V26_LOC = 0x2a40; 256 | uint256 internal constant C_V27_LOC = 0x2a60; 257 | uint256 internal constant C_V28_LOC = 0x2a80; 258 | uint256 internal constant C_V29_LOC = 0x2aa0; 259 | uint256 internal constant C_V30_LOC = 0x2ac0; 260 | 261 | uint256 internal constant C_U_LOC = 0x2b00; 262 | 263 | // ### LOCAL VARIABLES MEMORY OFFSETS 264 | uint256 internal constant DELTA_NUMERATOR_LOC = 0x3000; 265 | uint256 internal constant DELTA_DENOMINATOR_LOC = 0x3020; 266 | uint256 internal constant ZETA_POW_N_LOC = 0x3040; 267 | uint256 internal constant PUBLIC_INPUT_DELTA_LOC = 0x3060; 268 | uint256 internal constant ZERO_POLY_LOC = 0x3080; 269 | uint256 internal constant L_START_LOC = 0x30a0; 270 | uint256 internal constant L_END_LOC = 0x30c0; 271 | uint256 internal constant R_ZERO_EVAL_LOC = 0x30e0; 272 | 273 | uint256 internal constant PLOOKUP_DELTA_NUMERATOR_LOC = 0x3100; 274 | uint256 internal constant PLOOKUP_DELTA_DENOMINATOR_LOC = 0x3120; 275 | uint256 internal constant PLOOKUP_DELTA_LOC = 0x3140; 276 | 277 | uint256 internal constant ACCUMULATOR_X_LOC = 0x3160; 278 | uint256 internal constant ACCUMULATOR_Y_LOC = 0x3180; 279 | uint256 internal constant ACCUMULATOR2_X_LOC = 0x31a0; 280 | uint256 internal constant ACCUMULATOR2_Y_LOC = 0x31c0; 281 | uint256 internal constant PAIRING_LHS_X_LOC = 0x31e0; 282 | uint256 internal constant PAIRING_LHS_Y_LOC = 0x3200; 283 | uint256 internal constant PAIRING_RHS_X_LOC = 0x3220; 284 | uint256 internal constant PAIRING_RHS_Y_LOC = 0x3240; 285 | 286 | // ### SUCCESS FLAG MEMORY LOCATIONS 287 | uint256 internal constant GRAND_PRODUCT_SUCCESS_FLAG = 0x3300; 288 | uint256 internal constant ARITHMETIC_TERM_SUCCESS_FLAG = 0x3020; 289 | uint256 internal constant BATCH_OPENING_SUCCESS_FLAG = 0x3340; 290 | uint256 internal constant OPENING_COMMITMENT_SUCCESS_FLAG = 0x3360; 291 | uint256 internal constant PAIRING_PREAMBLE_SUCCESS_FLAG = 0x3380; 292 | uint256 internal constant PAIRING_SUCCESS_FLAG = 0x33a0; 293 | uint256 internal constant RESULT_FLAG = 0x33c0; 294 | 295 | // misc stuff 296 | uint256 internal constant OMEGA_INVERSE_LOC = 0x3400; 297 | uint256 internal constant C_ALPHA_SQR_LOC = 0x3420; 298 | uint256 internal constant C_ALPHA_CUBE_LOC = 0x3440; 299 | uint256 internal constant C_ALPHA_QUAD_LOC = 0x3460; 300 | uint256 internal constant C_ALPHA_BASE_LOC = 0x3480; 301 | 302 | // ### RECURSION VARIABLE MEMORY LOCATIONS 303 | uint256 internal constant RECURSIVE_P1_X_LOC = 0x3500; 304 | uint256 internal constant RECURSIVE_P1_Y_LOC = 0x3520; 305 | uint256 internal constant RECURSIVE_P2_X_LOC = 0x3540; 306 | uint256 internal constant RECURSIVE_P2_Y_LOC = 0x3560; 307 | 308 | uint256 internal constant PUBLIC_INPUTS_HASH_LOCATION = 0x3580; 309 | 310 | // sub-identity storage 311 | uint256 internal constant PERMUTATION_IDENTITY = 0x3600; 312 | uint256 internal constant PLOOKUP_IDENTITY = 0x3620; 313 | uint256 internal constant ARITHMETIC_IDENTITY = 0x3640; 314 | uint256 internal constant SORT_IDENTITY = 0x3660; 315 | uint256 internal constant ELLIPTIC_IDENTITY = 0x3680; 316 | uint256 internal constant AUX_IDENTITY = 0x36a0; 317 | uint256 internal constant AUX_NON_NATIVE_FIELD_EVALUATION = 0x36c0; 318 | uint256 internal constant AUX_LIMB_ACCUMULATOR_EVALUATION = 0x36e0; 319 | uint256 internal constant AUX_RAM_CONSISTENCY_EVALUATION = 0x3700; 320 | uint256 internal constant AUX_ROM_CONSISTENCY_EVALUATION = 0x3720; 321 | uint256 internal constant AUX_MEMORY_EVALUATION = 0x3740; 322 | 323 | uint256 internal constant QUOTIENT_EVAL_LOC = 0x3760; 324 | uint256 internal constant ZERO_POLY_INVERSE_LOC = 0x3780; 325 | 326 | // when hashing public inputs we use memory at NU_CHALLENGE_INPUT_LOC_A, as the hash input size is unknown at compile time 327 | uint256 internal constant NU_CHALLENGE_INPUT_LOC_A = 0x37a0; 328 | uint256 internal constant NU_CHALLENGE_INPUT_LOC_B = 0x37c0; 329 | uint256 internal constant NU_CHALLENGE_INPUT_LOC_C = 0x37e0; 330 | 331 | bytes4 internal constant PUBLIC_INPUT_INVALID_BN128_G1_POINT_SELECTOR = 0xeba9f4a6; 332 | bytes4 internal constant PUBLIC_INPUT_GE_P_SELECTOR = 0x374a972f; 333 | bytes4 internal constant MOD_EXP_FAILURE_SELECTOR = 0xf894a7bc; 334 | bytes4 internal constant EC_SCALAR_MUL_FAILURE_SELECTOR = 0xf755f369; 335 | bytes4 internal constant PROOF_FAILURE_SELECTOR = 0x0711fcec; 336 | 337 | uint256 internal constant ETA_INPUT_LENGTH = 0xc0; // W1, W2, W3 = 6 * 0x20 bytes 338 | 339 | // We need to hash 41 field elements when generating the NU challenge 340 | // w1, w2, w3, w4, s, z, z_lookup, q1, q2, q3, q4, qm, qc, qarith (14) 341 | // qsort, qelliptic, qaux, sigma1, sigma2, sigma, sigma4, (7) 342 | // table1, table2, table3, table4, tabletype, id1, id2, id3, id4, (9) 343 | // w1_omega, w2_omega, w3_omega, w4_omega, s_omega, z_omega, z_lookup_omega, (7) 344 | // table1_omega, table2_omega, table3_omega, table4_omega (4) 345 | uint256 internal constant NU_INPUT_LENGTH = 0x520; // 0x520 = 41 * 0x20 346 | 347 | // There are ELEVEN G1 group elements added into the transcript in the `beta` round, that we need to skip over 348 | // W1, W2, W3, W4, S, Z, Z_LOOKUP, T1, T2, T3, T4 349 | uint256 internal constant NU_CALLDATA_SKIP_LENGTH = 0x2c0; // 11 * 0x40 = 0x2c0 350 | 351 | uint256 internal constant NEGATIVE_INVERSE_OF_2_MODULO_P = 352 | 0x183227397098d014dc2822db40c0ac2e9419f4243cdcb848a1f0fac9f8000000; 353 | uint256 internal constant LIMB_SIZE = 0x100000000000000000; // 2<<68 354 | uint256 internal constant SUBLIMB_SHIFT = 0x4000; // 2<<14 355 | 356 | // y^2 = x^3 + ax + b 357 | // for Grumpkin, a = 0 and b = -17. We use b in a custom gate relation that evaluates elliptic curve arithmetic 358 | uint256 internal constant GRUMPKIN_CURVE_B_PARAMETER_NEGATED = 17; 359 | error PUBLIC_INPUT_COUNT_INVALID(uint256 expected, uint256 actual); 360 | error PUBLIC_INPUT_INVALID_BN128_G1_POINT(); 361 | error PUBLIC_INPUT_GE_P(); 362 | error MOD_EXP_FAILURE(); 363 | error EC_SCALAR_MUL_FAILURE(); 364 | error PROOF_FAILURE(); 365 | 366 | function getVerificationKeyHash() public pure virtual returns (bytes32); 367 | 368 | function loadVerificationKey(uint256 _vk, uint256 _omegaInverseLoc) internal pure virtual; 369 | 370 | /** 371 | * @notice Verify a Ultra Plonk proof 372 | * @param _proof - The serialized proof 373 | * @param _publicInputs - An array of the public inputs 374 | * @return True if proof is valid, reverts otherwise 375 | */ 376 | function verify(bytes calldata _proof, bytes32[] calldata _publicInputs) external view returns (bool) { 377 | loadVerificationKey(N_LOC, OMEGA_INVERSE_LOC); 378 | 379 | uint256 requiredPublicInputCount; 380 | assembly { 381 | requiredPublicInputCount := mload(NUM_INPUTS_LOC) 382 | } 383 | if (requiredPublicInputCount != _publicInputs.length) { 384 | revert PUBLIC_INPUT_COUNT_INVALID(requiredPublicInputCount, _publicInputs.length); 385 | } 386 | 387 | assembly { 388 | let q := 21888242871839275222246405745257275088696311157297823662689037894645226208583 // EC group order 389 | let p := 21888242871839275222246405745257275088548364400416034343698204186575808495617 // Prime field order 390 | 391 | /** 392 | * LOAD PROOF FROM CALLDATA 393 | */ 394 | { 395 | let data_ptr := add(calldataload(0x04), 0x24) 396 | 397 | mstore(W1_Y_LOC, mod(calldataload(data_ptr), q)) 398 | mstore(W1_X_LOC, mod(calldataload(add(data_ptr, 0x20)), q)) 399 | 400 | mstore(W2_Y_LOC, mod(calldataload(add(data_ptr, 0x40)), q)) 401 | mstore(W2_X_LOC, mod(calldataload(add(data_ptr, 0x60)), q)) 402 | 403 | mstore(W3_Y_LOC, mod(calldataload(add(data_ptr, 0x80)), q)) 404 | mstore(W3_X_LOC, mod(calldataload(add(data_ptr, 0xa0)), q)) 405 | 406 | mstore(W4_Y_LOC, mod(calldataload(add(data_ptr, 0xc0)), q)) 407 | mstore(W4_X_LOC, mod(calldataload(add(data_ptr, 0xe0)), q)) 408 | 409 | mstore(S_Y_LOC, mod(calldataload(add(data_ptr, 0x100)), q)) 410 | mstore(S_X_LOC, mod(calldataload(add(data_ptr, 0x120)), q)) 411 | mstore(Z_Y_LOC, mod(calldataload(add(data_ptr, 0x140)), q)) 412 | mstore(Z_X_LOC, mod(calldataload(add(data_ptr, 0x160)), q)) 413 | mstore(Z_LOOKUP_Y_LOC, mod(calldataload(add(data_ptr, 0x180)), q)) 414 | mstore(Z_LOOKUP_X_LOC, mod(calldataload(add(data_ptr, 0x1a0)), q)) 415 | mstore(T1_Y_LOC, mod(calldataload(add(data_ptr, 0x1c0)), q)) 416 | mstore(T1_X_LOC, mod(calldataload(add(data_ptr, 0x1e0)), q)) 417 | 418 | mstore(T2_Y_LOC, mod(calldataload(add(data_ptr, 0x200)), q)) 419 | mstore(T2_X_LOC, mod(calldataload(add(data_ptr, 0x220)), q)) 420 | 421 | mstore(T3_Y_LOC, mod(calldataload(add(data_ptr, 0x240)), q)) 422 | mstore(T3_X_LOC, mod(calldataload(add(data_ptr, 0x260)), q)) 423 | 424 | mstore(T4_Y_LOC, mod(calldataload(add(data_ptr, 0x280)), q)) 425 | mstore(T4_X_LOC, mod(calldataload(add(data_ptr, 0x2a0)), q)) 426 | 427 | mstore(W1_EVAL_LOC, mod(calldataload(add(data_ptr, 0x2c0)), p)) 428 | mstore(W2_EVAL_LOC, mod(calldataload(add(data_ptr, 0x2e0)), p)) 429 | mstore(W3_EVAL_LOC, mod(calldataload(add(data_ptr, 0x300)), p)) 430 | mstore(W4_EVAL_LOC, mod(calldataload(add(data_ptr, 0x320)), p)) 431 | mstore(S_EVAL_LOC, mod(calldataload(add(data_ptr, 0x340)), p)) 432 | mstore(Z_EVAL_LOC, mod(calldataload(add(data_ptr, 0x360)), p)) 433 | mstore(Z_LOOKUP_EVAL_LOC, mod(calldataload(add(data_ptr, 0x380)), p)) 434 | mstore(Q1_EVAL_LOC, mod(calldataload(add(data_ptr, 0x3a0)), p)) 435 | mstore(Q2_EVAL_LOC, mod(calldataload(add(data_ptr, 0x3c0)), p)) 436 | mstore(Q3_EVAL_LOC, mod(calldataload(add(data_ptr, 0x3e0)), p)) 437 | mstore(Q4_EVAL_LOC, mod(calldataload(add(data_ptr, 0x400)), p)) 438 | mstore(QM_EVAL_LOC, mod(calldataload(add(data_ptr, 0x420)), p)) 439 | mstore(QC_EVAL_LOC, mod(calldataload(add(data_ptr, 0x440)), p)) 440 | mstore(QARITH_EVAL_LOC, mod(calldataload(add(data_ptr, 0x460)), p)) 441 | mstore(QSORT_EVAL_LOC, mod(calldataload(add(data_ptr, 0x480)), p)) 442 | mstore(QELLIPTIC_EVAL_LOC, mod(calldataload(add(data_ptr, 0x4a0)), p)) 443 | mstore(QAUX_EVAL_LOC, mod(calldataload(add(data_ptr, 0x4c0)), p)) 444 | 445 | mstore(SIGMA1_EVAL_LOC, mod(calldataload(add(data_ptr, 0x4e0)), p)) 446 | mstore(SIGMA2_EVAL_LOC, mod(calldataload(add(data_ptr, 0x500)), p)) 447 | 448 | mstore(SIGMA3_EVAL_LOC, mod(calldataload(add(data_ptr, 0x520)), p)) 449 | mstore(SIGMA4_EVAL_LOC, mod(calldataload(add(data_ptr, 0x540)), p)) 450 | 451 | mstore(TABLE1_EVAL_LOC, mod(calldataload(add(data_ptr, 0x560)), p)) 452 | mstore(TABLE2_EVAL_LOC, mod(calldataload(add(data_ptr, 0x580)), p)) 453 | mstore(TABLE3_EVAL_LOC, mod(calldataload(add(data_ptr, 0x5a0)), p)) 454 | mstore(TABLE4_EVAL_LOC, mod(calldataload(add(data_ptr, 0x5c0)), p)) 455 | mstore(TABLE_TYPE_EVAL_LOC, mod(calldataload(add(data_ptr, 0x5e0)), p)) 456 | 457 | mstore(ID1_EVAL_LOC, mod(calldataload(add(data_ptr, 0x600)), p)) 458 | mstore(ID2_EVAL_LOC, mod(calldataload(add(data_ptr, 0x620)), p)) 459 | mstore(ID3_EVAL_LOC, mod(calldataload(add(data_ptr, 0x640)), p)) 460 | mstore(ID4_EVAL_LOC, mod(calldataload(add(data_ptr, 0x660)), p)) 461 | 462 | mstore(W1_OMEGA_EVAL_LOC, mod(calldataload(add(data_ptr, 0x680)), p)) 463 | mstore(W2_OMEGA_EVAL_LOC, mod(calldataload(add(data_ptr, 0x6a0)), p)) 464 | mstore(W3_OMEGA_EVAL_LOC, mod(calldataload(add(data_ptr, 0x6c0)), p)) 465 | mstore(W4_OMEGA_EVAL_LOC, mod(calldataload(add(data_ptr, 0x6e0)), p)) 466 | mstore(S_OMEGA_EVAL_LOC, mod(calldataload(add(data_ptr, 0x700)), p)) 467 | 468 | mstore(Z_OMEGA_EVAL_LOC, mod(calldataload(add(data_ptr, 0x720)), p)) 469 | 470 | mstore(Z_LOOKUP_OMEGA_EVAL_LOC, mod(calldataload(add(data_ptr, 0x740)), p)) 471 | mstore(TABLE1_OMEGA_EVAL_LOC, mod(calldataload(add(data_ptr, 0x760)), p)) 472 | mstore(TABLE2_OMEGA_EVAL_LOC, mod(calldataload(add(data_ptr, 0x780)), p)) 473 | mstore(TABLE3_OMEGA_EVAL_LOC, mod(calldataload(add(data_ptr, 0x7a0)), p)) 474 | mstore(TABLE4_OMEGA_EVAL_LOC, mod(calldataload(add(data_ptr, 0x7c0)), p)) 475 | 476 | mstore(PI_Z_Y_LOC, mod(calldataload(add(data_ptr, 0x7e0)), q)) 477 | mstore(PI_Z_X_LOC, mod(calldataload(add(data_ptr, 0x800)), q)) 478 | 479 | mstore(PI_Z_OMEGA_Y_LOC, mod(calldataload(add(data_ptr, 0x820)), q)) 480 | mstore(PI_Z_OMEGA_X_LOC, mod(calldataload(add(data_ptr, 0x840)), q)) 481 | } 482 | 483 | /** 484 | * LOAD RECURSIVE PROOF INTO MEMORY 485 | */ 486 | { 487 | if mload(CONTAINS_RECURSIVE_PROOF_LOC) { 488 | let public_inputs_ptr := add(calldataload(0x24), 0x24) 489 | let index_counter := add(shl(5, mload(RECURSIVE_PROOF_PUBLIC_INPUT_INDICES_LOC)), public_inputs_ptr) 490 | 491 | let x0 := calldataload(index_counter) 492 | x0 := add(x0, shl(68, calldataload(add(index_counter, 0x20)))) 493 | x0 := add(x0, shl(136, calldataload(add(index_counter, 0x40)))) 494 | x0 := add(x0, shl(204, calldataload(add(index_counter, 0x60)))) 495 | let y0 := calldataload(add(index_counter, 0x80)) 496 | y0 := add(y0, shl(68, calldataload(add(index_counter, 0xa0)))) 497 | y0 := add(y0, shl(136, calldataload(add(index_counter, 0xc0)))) 498 | y0 := add(y0, shl(204, calldataload(add(index_counter, 0xe0)))) 499 | let x1 := calldataload(add(index_counter, 0x100)) 500 | x1 := add(x1, shl(68, calldataload(add(index_counter, 0x120)))) 501 | x1 := add(x1, shl(136, calldataload(add(index_counter, 0x140)))) 502 | x1 := add(x1, shl(204, calldataload(add(index_counter, 0x160)))) 503 | let y1 := calldataload(add(index_counter, 0x180)) 504 | y1 := add(y1, shl(68, calldataload(add(index_counter, 0x1a0)))) 505 | y1 := add(y1, shl(136, calldataload(add(index_counter, 0x1c0)))) 506 | y1 := add(y1, shl(204, calldataload(add(index_counter, 0x1e0)))) 507 | mstore(RECURSIVE_P1_X_LOC, x0) 508 | mstore(RECURSIVE_P1_Y_LOC, y0) 509 | mstore(RECURSIVE_P2_X_LOC, x1) 510 | mstore(RECURSIVE_P2_Y_LOC, y1) 511 | 512 | // validate these are valid bn128 G1 points 513 | if iszero(and(and(lt(x0, q), lt(x1, q)), and(lt(y0, q), lt(y1, q)))) { 514 | mstore(0x00, PUBLIC_INPUT_INVALID_BN128_G1_POINT_SELECTOR) 515 | revert(0x00, 0x04) 516 | } 517 | } 518 | } 519 | 520 | { 521 | /** 522 | * Generate initial challenge 523 | */ 524 | mstore(0x00, shl(224, mload(N_LOC))) 525 | mstore(0x04, shl(224, mload(NUM_INPUTS_LOC))) 526 | let challenge := keccak256(0x00, 0x08) 527 | 528 | /** 529 | * Generate eta challenge 530 | */ 531 | mstore(PUBLIC_INPUTS_HASH_LOCATION, challenge) 532 | // The public input location is stored at 0x24, we then add 0x24 to skip selector and the length of public inputs 533 | let public_inputs_start := add(calldataload(0x24), 0x24) 534 | // copy the public inputs over 535 | let public_input_size := mul(mload(NUM_INPUTS_LOC), 0x20) 536 | calldatacopy(add(PUBLIC_INPUTS_HASH_LOCATION, 0x20), public_inputs_start, public_input_size) 537 | 538 | // copy W1, W2, W3 into challenge. Each point is 0x40 bytes, so load 0xc0 = 3 * 0x40 bytes (ETA input length) 539 | let w_start := add(calldataload(0x04), 0x24) 540 | calldatacopy(add(add(PUBLIC_INPUTS_HASH_LOCATION, 0x20), public_input_size), w_start, ETA_INPUT_LENGTH) 541 | 542 | // Challenge is the old challenge + public inputs + W1, W2, W3 (0x20 + public_input_size + 0xc0) 543 | let challenge_bytes_size := add(0x20, add(public_input_size, ETA_INPUT_LENGTH)) 544 | 545 | challenge := keccak256(PUBLIC_INPUTS_HASH_LOCATION, challenge_bytes_size) 546 | { 547 | let eta := mod(challenge, p) 548 | mstore(C_ETA_LOC, eta) 549 | mstore(C_ETA_SQR_LOC, mulmod(eta, eta, p)) 550 | mstore(C_ETA_CUBE_LOC, mulmod(mload(C_ETA_SQR_LOC), eta, p)) 551 | } 552 | 553 | /** 554 | * Generate beta challenge 555 | */ 556 | mstore(0x00, challenge) 557 | mstore(0x20, mload(W4_Y_LOC)) 558 | mstore(0x40, mload(W4_X_LOC)) 559 | mstore(0x60, mload(S_Y_LOC)) 560 | mstore(0x80, mload(S_X_LOC)) 561 | challenge := keccak256(0x00, 0xa0) 562 | mstore(C_BETA_LOC, mod(challenge, p)) 563 | 564 | /** 565 | * Generate gamma challenge 566 | */ 567 | mstore(0x00, challenge) 568 | mstore8(0x20, 0x01) 569 | challenge := keccak256(0x00, 0x21) 570 | mstore(C_GAMMA_LOC, mod(challenge, p)) 571 | 572 | /** 573 | * Generate alpha challenge 574 | */ 575 | mstore(0x00, challenge) 576 | mstore(0x20, mload(Z_Y_LOC)) 577 | mstore(0x40, mload(Z_X_LOC)) 578 | mstore(0x60, mload(Z_LOOKUP_Y_LOC)) 579 | mstore(0x80, mload(Z_LOOKUP_X_LOC)) 580 | challenge := keccak256(0x00, 0xa0) 581 | mstore(C_ALPHA_LOC, mod(challenge, p)) 582 | 583 | /** 584 | * Compute and store some powers of alpha for future computations 585 | */ 586 | let alpha := mload(C_ALPHA_LOC) 587 | mstore(C_ALPHA_SQR_LOC, mulmod(alpha, alpha, p)) 588 | mstore(C_ALPHA_CUBE_LOC, mulmod(mload(C_ALPHA_SQR_LOC), alpha, p)) 589 | mstore(C_ALPHA_QUAD_LOC, mulmod(mload(C_ALPHA_CUBE_LOC), alpha, p)) 590 | mstore(C_ALPHA_BASE_LOC, alpha) 591 | 592 | /** 593 | * Generate zeta challenge 594 | */ 595 | mstore(0x00, challenge) 596 | mstore(0x20, mload(T1_Y_LOC)) 597 | mstore(0x40, mload(T1_X_LOC)) 598 | mstore(0x60, mload(T2_Y_LOC)) 599 | mstore(0x80, mload(T2_X_LOC)) 600 | mstore(0xa0, mload(T3_Y_LOC)) 601 | mstore(0xc0, mload(T3_X_LOC)) 602 | mstore(0xe0, mload(T4_Y_LOC)) 603 | mstore(0x100, mload(T4_X_LOC)) 604 | 605 | challenge := keccak256(0x00, 0x120) 606 | 607 | mstore(C_ZETA_LOC, mod(challenge, p)) 608 | mstore(C_CURRENT_LOC, challenge) 609 | } 610 | 611 | /** 612 | * EVALUATE FIELD OPERATIONS 613 | */ 614 | 615 | /** 616 | * COMPUTE PUBLIC INPUT DELTA 617 | * ΔPI = ∏ᵢ∈ℓ(wᵢ + β σ(i) + γ) / ∏ᵢ∈ℓ(wᵢ + β σ'(i) + γ) 618 | */ 619 | { 620 | let beta := mload(C_BETA_LOC) // β 621 | let gamma := mload(C_GAMMA_LOC) // γ 622 | let work_root := mload(OMEGA_LOC) // ω 623 | let numerator_value := 1 624 | let denominator_value := 1 625 | 626 | let p_clone := p // move p to the front of the stack 627 | let valid_inputs := true 628 | 629 | // Load the starting point of the public inputs (jump over the selector and the length of public inputs [0x24]) 630 | let public_inputs_ptr := add(calldataload(0x24), 0x24) 631 | 632 | // endpoint_ptr = public_inputs_ptr + num_inputs * 0x20. // every public input is 0x20 bytes 633 | let endpoint_ptr := add(public_inputs_ptr, mul(mload(NUM_INPUTS_LOC), 0x20)) 634 | 635 | // root_1 = β * 0x05 636 | let root_1 := mulmod(beta, 0x05, p_clone) // k1.β 637 | // root_2 = β * 0x0c 638 | let root_2 := mulmod(beta, 0x0c, p_clone) 639 | // @note 0x05 + 0x07 == 0x0c == external coset generator 640 | 641 | for {} lt(public_inputs_ptr, endpoint_ptr) { public_inputs_ptr := add(public_inputs_ptr, 0x20) } { 642 | /** 643 | * input = public_input[i] 644 | * valid_inputs &= input < p 645 | * temp = input + gamma 646 | * numerator_value *= (β.σ(i) + wᵢ + γ) // σ(i) = 0x05.ωⁱ 647 | * denominator_value *= (β.σ'(i) + wᵢ + γ) // σ'(i) = 0x0c.ωⁱ 648 | * root_1 *= ω 649 | * root_2 *= ω 650 | */ 651 | 652 | let input := calldataload(public_inputs_ptr) 653 | valid_inputs := and(valid_inputs, lt(input, p_clone)) 654 | let temp := addmod(input, gamma, p_clone) 655 | 656 | numerator_value := mulmod(numerator_value, add(root_1, temp), p_clone) 657 | denominator_value := mulmod(denominator_value, add(root_2, temp), p_clone) 658 | 659 | root_1 := mulmod(root_1, work_root, p_clone) 660 | root_2 := mulmod(root_2, work_root, p_clone) 661 | } 662 | 663 | // Revert if not all public inputs are field elements (i.e. < p) 664 | if iszero(valid_inputs) { 665 | mstore(0x00, PUBLIC_INPUT_GE_P_SELECTOR) 666 | revert(0x00, 0x04) 667 | } 668 | 669 | mstore(DELTA_NUMERATOR_LOC, numerator_value) 670 | mstore(DELTA_DENOMINATOR_LOC, denominator_value) 671 | } 672 | 673 | /** 674 | * Compute Plookup delta factor [γ(1 + β)]^{n-k} 675 | * k = num roots cut out of Z_H = 4 676 | */ 677 | { 678 | let delta_base := mulmod(mload(C_GAMMA_LOC), addmod(mload(C_BETA_LOC), 1, p), p) 679 | let delta_numerator := delta_base 680 | { 681 | let exponent := mload(N_LOC) 682 | let count := 1 683 | for {} lt(count, exponent) { count := add(count, count) } { 684 | delta_numerator := mulmod(delta_numerator, delta_numerator, p) 685 | } 686 | } 687 | mstore(PLOOKUP_DELTA_NUMERATOR_LOC, delta_numerator) 688 | 689 | let delta_denominator := mulmod(delta_base, delta_base, p) 690 | delta_denominator := mulmod(delta_denominator, delta_denominator, p) 691 | mstore(PLOOKUP_DELTA_DENOMINATOR_LOC, delta_denominator) 692 | } 693 | /** 694 | * Compute lagrange poly and vanishing poly fractions 695 | */ 696 | { 697 | /** 698 | * vanishing_numerator = zeta 699 | * ZETA_POW_N = zeta^n 700 | * vanishing_numerator -= 1 701 | * accumulating_root = omega_inverse 702 | * work_root = p - accumulating_root 703 | * domain_inverse = domain_inverse 704 | * vanishing_denominator = zeta + work_root 705 | * work_root *= accumulating_root 706 | * vanishing_denominator *= (zeta + work_root) 707 | * work_root *= accumulating_root 708 | * vanishing_denominator *= (zeta + work_root) 709 | * vanishing_denominator *= (zeta + (zeta + accumulating_root)) 710 | * work_root = omega 711 | * lagrange_numerator = vanishing_numerator * domain_inverse 712 | * l_start_denominator = zeta - 1 713 | * accumulating_root = work_root^2 714 | * l_end_denominator = accumulating_root^2 * work_root * zeta - 1 715 | * Note: l_end_denominator term contains a term \omega^5 to cut out 5 roots of unity from vanishing poly 716 | */ 717 | 718 | let zeta := mload(C_ZETA_LOC) 719 | 720 | // compute zeta^n, where n is a power of 2 721 | let vanishing_numerator := zeta 722 | { 723 | // pow_small 724 | let exponent := mload(N_LOC) 725 | let count := 1 726 | for {} lt(count, exponent) { count := add(count, count) } { 727 | vanishing_numerator := mulmod(vanishing_numerator, vanishing_numerator, p) 728 | } 729 | } 730 | mstore(ZETA_POW_N_LOC, vanishing_numerator) 731 | vanishing_numerator := addmod(vanishing_numerator, sub(p, 1), p) 732 | 733 | let accumulating_root := mload(OMEGA_INVERSE_LOC) 734 | let work_root := sub(p, accumulating_root) 735 | let domain_inverse := mload(DOMAIN_INVERSE_LOC) 736 | 737 | let vanishing_denominator := addmod(zeta, work_root, p) 738 | work_root := mulmod(work_root, accumulating_root, p) 739 | vanishing_denominator := mulmod(vanishing_denominator, addmod(zeta, work_root, p), p) 740 | work_root := mulmod(work_root, accumulating_root, p) 741 | vanishing_denominator := mulmod(vanishing_denominator, addmod(zeta, work_root, p), p) 742 | vanishing_denominator := 743 | mulmod(vanishing_denominator, addmod(zeta, mulmod(work_root, accumulating_root, p), p), p) 744 | 745 | work_root := mload(OMEGA_LOC) 746 | 747 | let lagrange_numerator := mulmod(vanishing_numerator, domain_inverse, p) 748 | let l_start_denominator := addmod(zeta, sub(p, 1), p) 749 | 750 | accumulating_root := mulmod(work_root, work_root, p) 751 | 752 | let l_end_denominator := 753 | addmod( 754 | mulmod(mulmod(mulmod(accumulating_root, accumulating_root, p), work_root, p), zeta, p), sub(p, 1), p 755 | ) 756 | 757 | /** 758 | * Compute inversions using Montgomery's batch inversion trick 759 | */ 760 | let accumulator := mload(DELTA_DENOMINATOR_LOC) 761 | let t0 := accumulator 762 | accumulator := mulmod(accumulator, vanishing_denominator, p) 763 | let t1 := accumulator 764 | accumulator := mulmod(accumulator, vanishing_numerator, p) 765 | let t2 := accumulator 766 | accumulator := mulmod(accumulator, l_start_denominator, p) 767 | let t3 := accumulator 768 | accumulator := mulmod(accumulator, mload(PLOOKUP_DELTA_DENOMINATOR_LOC), p) 769 | let t4 := accumulator 770 | { 771 | mstore(0, 0x20) 772 | mstore(0x20, 0x20) 773 | mstore(0x40, 0x20) 774 | mstore(0x60, mulmod(accumulator, l_end_denominator, p)) 775 | mstore(0x80, sub(p, 2)) 776 | mstore(0xa0, p) 777 | if iszero(staticcall(gas(), 0x05, 0x00, 0xc0, 0x00, 0x20)) { 778 | mstore(0x0, MOD_EXP_FAILURE_SELECTOR) 779 | revert(0x00, 0x04) 780 | } 781 | accumulator := mload(0x00) 782 | } 783 | 784 | t4 := mulmod(accumulator, t4, p) 785 | accumulator := mulmod(accumulator, l_end_denominator, p) 786 | 787 | t3 := mulmod(accumulator, t3, p) 788 | accumulator := mulmod(accumulator, mload(PLOOKUP_DELTA_DENOMINATOR_LOC), p) 789 | 790 | t2 := mulmod(accumulator, t2, p) 791 | accumulator := mulmod(accumulator, l_start_denominator, p) 792 | 793 | t1 := mulmod(accumulator, t1, p) 794 | accumulator := mulmod(accumulator, vanishing_numerator, p) 795 | 796 | t0 := mulmod(accumulator, t0, p) 797 | accumulator := mulmod(accumulator, vanishing_denominator, p) 798 | 799 | accumulator := mulmod(mulmod(accumulator, accumulator, p), mload(DELTA_DENOMINATOR_LOC), p) 800 | 801 | mstore(PUBLIC_INPUT_DELTA_LOC, mulmod(mload(DELTA_NUMERATOR_LOC), accumulator, p)) 802 | mstore(ZERO_POLY_LOC, mulmod(vanishing_numerator, t0, p)) 803 | mstore(ZERO_POLY_INVERSE_LOC, mulmod(vanishing_denominator, t1, p)) 804 | mstore(L_START_LOC, mulmod(lagrange_numerator, t2, p)) 805 | mstore(PLOOKUP_DELTA_LOC, mulmod(mload(PLOOKUP_DELTA_NUMERATOR_LOC), t3, p)) 806 | mstore(L_END_LOC, mulmod(lagrange_numerator, t4, p)) 807 | } 808 | 809 | /** 810 | * UltraPlonk Widget Ordering: 811 | * 812 | * 1. Permutation widget 813 | * 2. Plookup widget 814 | * 3. Arithmetic widget 815 | * 4. Fixed base widget (?) 816 | * 5. GenPermSort widget 817 | * 6. Elliptic widget 818 | * 7. Auxiliary widget 819 | */ 820 | 821 | /** 822 | * COMPUTE PERMUTATION WIDGET EVALUATION 823 | */ 824 | { 825 | let alpha := mload(C_ALPHA_LOC) 826 | let beta := mload(C_BETA_LOC) 827 | let gamma := mload(C_GAMMA_LOC) 828 | 829 | /** 830 | * t1 = (W1 + gamma + beta * ID1) * (W2 + gamma + beta * ID2) 831 | * t2 = (W3 + gamma + beta * ID3) * (W4 + gamma + beta * ID4) 832 | * result = alpha_base * z_eval * t1 * t2 833 | * t1 = (W1 + gamma + beta * sigma_1_eval) * (W2 + gamma + beta * sigma_2_eval) 834 | * t2 = (W2 + gamma + beta * sigma_3_eval) * (W3 + gamma + beta * sigma_4_eval) 835 | * result -= (alpha_base * z_omega_eval * t1 * t2) 836 | */ 837 | let t1 := 838 | mulmod( 839 | add(add(mload(W1_EVAL_LOC), gamma), mulmod(beta, mload(ID1_EVAL_LOC), p)), 840 | add(add(mload(W2_EVAL_LOC), gamma), mulmod(beta, mload(ID2_EVAL_LOC), p)), 841 | p 842 | ) 843 | let t2 := 844 | mulmod( 845 | add(add(mload(W3_EVAL_LOC), gamma), mulmod(beta, mload(ID3_EVAL_LOC), p)), 846 | add(add(mload(W4_EVAL_LOC), gamma), mulmod(beta, mload(ID4_EVAL_LOC), p)), 847 | p 848 | ) 849 | let result := mulmod(mload(C_ALPHA_BASE_LOC), mulmod(mload(Z_EVAL_LOC), mulmod(t1, t2, p), p), p) 850 | t1 := 851 | mulmod( 852 | add(add(mload(W1_EVAL_LOC), gamma), mulmod(beta, mload(SIGMA1_EVAL_LOC), p)), 853 | add(add(mload(W2_EVAL_LOC), gamma), mulmod(beta, mload(SIGMA2_EVAL_LOC), p)), 854 | p 855 | ) 856 | t2 := 857 | mulmod( 858 | add(add(mload(W3_EVAL_LOC), gamma), mulmod(beta, mload(SIGMA3_EVAL_LOC), p)), 859 | add(add(mload(W4_EVAL_LOC), gamma), mulmod(beta, mload(SIGMA4_EVAL_LOC), p)), 860 | p 861 | ) 862 | result := 863 | addmod( 864 | result, 865 | sub(p, mulmod(mload(C_ALPHA_BASE_LOC), mulmod(mload(Z_OMEGA_EVAL_LOC), mulmod(t1, t2, p), p), p)), 866 | p 867 | ) 868 | 869 | /** 870 | * alpha_base *= alpha 871 | * result += alpha_base . (L_{n-k}(ʓ) . (z(ʓ.ω) - ∆_{PI})) 872 | * alpha_base *= alpha 873 | * result += alpha_base . (L_1(ʓ)(Z(ʓ) - 1)) 874 | * alpha_Base *= alpha 875 | */ 876 | mstore(C_ALPHA_BASE_LOC, mulmod(mload(C_ALPHA_BASE_LOC), mload(C_ALPHA_LOC), p)) 877 | result := 878 | addmod( 879 | result, 880 | mulmod( 881 | mload(C_ALPHA_BASE_LOC), 882 | mulmod( 883 | mload(L_END_LOC), 884 | addmod(mload(Z_OMEGA_EVAL_LOC), sub(p, mload(PUBLIC_INPUT_DELTA_LOC)), p), 885 | p 886 | ), 887 | p 888 | ), 889 | p 890 | ) 891 | mstore(C_ALPHA_BASE_LOC, mulmod(mload(C_ALPHA_BASE_LOC), mload(C_ALPHA_LOC), p)) 892 | mstore( 893 | PERMUTATION_IDENTITY, 894 | addmod( 895 | result, 896 | mulmod( 897 | mload(C_ALPHA_BASE_LOC), 898 | mulmod(mload(L_START_LOC), addmod(mload(Z_EVAL_LOC), sub(p, 1), p), p), 899 | p 900 | ), 901 | p 902 | ) 903 | ) 904 | mstore(C_ALPHA_BASE_LOC, mulmod(mload(C_ALPHA_BASE_LOC), mload(C_ALPHA_LOC), p)) 905 | } 906 | 907 | /** 908 | * COMPUTE PLOOKUP WIDGET EVALUATION 909 | */ 910 | { 911 | /** 912 | * Goal: f = (w1(z) + q2.w1(zω)) + η(w2(z) + qm.w2(zω)) + η²(w3(z) + qc.w_3(zω)) + q3(z).η³ 913 | * f = η.q3(z) 914 | * f += (w3(z) + qc.w_3(zω)) 915 | * f *= η 916 | * f += (w2(z) + qm.w2(zω)) 917 | * f *= η 918 | * f += (w1(z) + q2.w1(zω)) 919 | */ 920 | let f := mulmod(mload(C_ETA_LOC), mload(Q3_EVAL_LOC), p) 921 | f := 922 | addmod(f, addmod(mload(W3_EVAL_LOC), mulmod(mload(QC_EVAL_LOC), mload(W3_OMEGA_EVAL_LOC), p), p), p) 923 | f := mulmod(f, mload(C_ETA_LOC), p) 924 | f := 925 | addmod(f, addmod(mload(W2_EVAL_LOC), mulmod(mload(QM_EVAL_LOC), mload(W2_OMEGA_EVAL_LOC), p), p), p) 926 | f := mulmod(f, mload(C_ETA_LOC), p) 927 | f := 928 | addmod(f, addmod(mload(W1_EVAL_LOC), mulmod(mload(Q2_EVAL_LOC), mload(W1_OMEGA_EVAL_LOC), p), p), p) 929 | 930 | // t(z) = table4(z).η³ + table3(z).η² + table2(z).η + table1(z) 931 | let t := 932 | addmod( 933 | addmod( 934 | addmod( 935 | mulmod(mload(TABLE4_EVAL_LOC), mload(C_ETA_CUBE_LOC), p), 936 | mulmod(mload(TABLE3_EVAL_LOC), mload(C_ETA_SQR_LOC), p), 937 | p 938 | ), 939 | mulmod(mload(TABLE2_EVAL_LOC), mload(C_ETA_LOC), p), 940 | p 941 | ), 942 | mload(TABLE1_EVAL_LOC), 943 | p 944 | ) 945 | 946 | // t(zw) = table4(zw).η³ + table3(zw).η² + table2(zw).η + table1(zw) 947 | let t_omega := 948 | addmod( 949 | addmod( 950 | addmod( 951 | mulmod(mload(TABLE4_OMEGA_EVAL_LOC), mload(C_ETA_CUBE_LOC), p), 952 | mulmod(mload(TABLE3_OMEGA_EVAL_LOC), mload(C_ETA_SQR_LOC), p), 953 | p 954 | ), 955 | mulmod(mload(TABLE2_OMEGA_EVAL_LOC), mload(C_ETA_LOC), p), 956 | p 957 | ), 958 | mload(TABLE1_OMEGA_EVAL_LOC), 959 | p 960 | ) 961 | 962 | /** 963 | * Goal: numerator = (TABLE_TYPE_EVAL * f(z) + γ) * (t(z) + βt(zω) + γ(β + 1)) * (β + 1) 964 | * gamma_beta_constant = γ(β + 1) 965 | * numerator = f * TABLE_TYPE_EVAL + gamma 966 | * temp0 = t(z) + t(zω) * β + gamma_beta_constant 967 | * numerator *= temp0 968 | * numerator *= (β + 1) 969 | * temp0 = alpha * l_1 970 | * numerator += temp0 971 | * numerator *= z_lookup(z) 972 | * numerator -= temp0 973 | */ 974 | let gamma_beta_constant := mulmod(mload(C_GAMMA_LOC), addmod(mload(C_BETA_LOC), 1, p), p) 975 | let numerator := addmod(mulmod(f, mload(TABLE_TYPE_EVAL_LOC), p), mload(C_GAMMA_LOC), p) 976 | let temp0 := addmod(addmod(t, mulmod(t_omega, mload(C_BETA_LOC), p), p), gamma_beta_constant, p) 977 | numerator := mulmod(numerator, temp0, p) 978 | numerator := mulmod(numerator, addmod(mload(C_BETA_LOC), 1, p), p) 979 | temp0 := mulmod(mload(C_ALPHA_LOC), mload(L_START_LOC), p) 980 | numerator := addmod(numerator, temp0, p) 981 | numerator := mulmod(numerator, mload(Z_LOOKUP_EVAL_LOC), p) 982 | numerator := addmod(numerator, sub(p, temp0), p) 983 | 984 | /** 985 | * Goal: denominator = z_lookup(zω)*[s(z) + βs(zω) + γ(1 + β)] - [z_lookup(zω) - [γ(1 + β)]^{n-k}]*α²L_end(z) 986 | * note: delta_factor = [γ(1 + β)]^{n-k} 987 | * denominator = s(z) + βs(zω) + γ(β + 1) 988 | * temp1 = α²L_end(z) 989 | * denominator -= temp1 990 | * denominator *= z_lookup(zω) 991 | * denominator += temp1 * delta_factor 992 | * PLOOKUP_IDENTITY = (numerator - denominator).alpha_base 993 | * alpha_base *= alpha^3 994 | */ 995 | let denominator := 996 | addmod( 997 | addmod(mload(S_EVAL_LOC), mulmod(mload(S_OMEGA_EVAL_LOC), mload(C_BETA_LOC), p), p), 998 | gamma_beta_constant, 999 | p 1000 | ) 1001 | let temp1 := mulmod(mload(C_ALPHA_SQR_LOC), mload(L_END_LOC), p) 1002 | denominator := addmod(denominator, sub(p, temp1), p) 1003 | denominator := mulmod(denominator, mload(Z_LOOKUP_OMEGA_EVAL_LOC), p) 1004 | denominator := addmod(denominator, mulmod(temp1, mload(PLOOKUP_DELTA_LOC), p), p) 1005 | 1006 | mstore(PLOOKUP_IDENTITY, mulmod(addmod(numerator, sub(p, denominator), p), mload(C_ALPHA_BASE_LOC), p)) 1007 | 1008 | // update alpha 1009 | mstore(C_ALPHA_BASE_LOC, mulmod(mload(C_ALPHA_BASE_LOC), mload(C_ALPHA_CUBE_LOC), p)) 1010 | } 1011 | 1012 | /** 1013 | * COMPUTE ARITHMETIC WIDGET EVALUATION 1014 | */ 1015 | { 1016 | /** 1017 | * The basic arithmetic gate identity in standard plonk is as follows. 1018 | * (w_1 . w_2 . q_m) + (w_1 . q_1) + (w_2 . q_2) + (w_3 . q_3) + (w_4 . q_4) + q_c = 0 1019 | * However, for Ultraplonk, we extend this to support "passing" wires between rows (shown without alpha scaling below): 1020 | * q_arith * ( ( (-1/2) * (q_arith - 3) * q_m * w_1 * w_2 + q_1 * w_1 + q_2 * w_2 + q_3 * w_3 + q_4 * w_4 + q_c ) + 1021 | * (q_arith - 1)*( α * (q_arith - 2) * (w_1 + w_4 - w_1_omega + q_m) + w_4_omega) ) = 0 1022 | * 1023 | * This formula results in several cases depending on q_arith: 1024 | * 1. q_arith == 0: Arithmetic gate is completely disabled 1025 | * 1026 | * 2. q_arith == 1: Everything in the minigate on the right is disabled. The equation is just a standard plonk equation 1027 | * with extra wires: q_m * w_1 * w_2 + q_1 * w_1 + q_2 * w_2 + q_3 * w_3 + q_4 * w_4 + q_c = 0 1028 | * 1029 | * 3. q_arith == 2: The (w_1 + w_4 - ...) term is disabled. THe equation is: 1030 | * (1/2) * q_m * w_1 * w_2 + q_1 * w_1 + q_2 * w_2 + q_3 * w_3 + q_4 * w_4 + q_c + w_4_omega = 0 1031 | * It allows defining w_4 at next index (w_4_omega) in terms of current wire values 1032 | * 1033 | * 4. q_arith == 3: The product of w_1 and w_2 is disabled, but a mini addition gate is enabled. α allows us to split 1034 | * the equation into two: 1035 | * 1036 | * q_1 * w_1 + q_2 * w_2 + q_3 * w_3 + q_4 * w_4 + q_c + 2 * w_4_omega = 0 1037 | * and 1038 | * w_1 + w_4 - w_1_omega + q_m = 0 (we are reusing q_m here) 1039 | * 1040 | * 5. q_arith > 3: The product of w_1 and w_2 is scaled by (q_arith - 3), while the w_4_omega term is scaled by (q_arith - 1). 1041 | * The equation can be split into two: 1042 | * 1043 | * (q_arith - 3)* q_m * w_1 * w_ 2 + q_1 * w_1 + q_2 * w_2 + q_3 * w_3 + q_4 * w_4 + q_c + (q_arith - 1) * w_4_omega = 0 1044 | * and 1045 | * w_1 + w_4 - w_1_omega + q_m = 0 1046 | * 1047 | * The problem that q_m is used both in both equations can be dealt with by appropriately changing selector values at 1048 | * the next gate. Then we can treat (q_arith - 1) as a simulated q_6 selector and scale q_m to handle (q_arith - 3) at 1049 | * product. 1050 | */ 1051 | 1052 | let w1q1 := mulmod(mload(W1_EVAL_LOC), mload(Q1_EVAL_LOC), p) 1053 | let w2q2 := mulmod(mload(W2_EVAL_LOC), mload(Q2_EVAL_LOC), p) 1054 | let w3q3 := mulmod(mload(W3_EVAL_LOC), mload(Q3_EVAL_LOC), p) 1055 | let w4q3 := mulmod(mload(W4_EVAL_LOC), mload(Q4_EVAL_LOC), p) 1056 | 1057 | // @todo - Add a explicit test that hits QARITH == 3 1058 | // w1w2qm := (w_1 . w_2 . q_m . (QARITH_EVAL_LOC - 3)) / 2 1059 | let w1w2qm := 1060 | mulmod( 1061 | mulmod( 1062 | mulmod(mulmod(mload(W1_EVAL_LOC), mload(W2_EVAL_LOC), p), mload(QM_EVAL_LOC), p), 1063 | addmod(mload(QARITH_EVAL_LOC), sub(p, 3), p), 1064 | p 1065 | ), 1066 | NEGATIVE_INVERSE_OF_2_MODULO_P, 1067 | p 1068 | ) 1069 | 1070 | // (w_1 . w_2 . q_m . (q_arith - 3)) / -2) + (w_1 . q_1) + (w_2 . q_2) + (w_3 . q_3) + (w_4 . q_4) + q_c 1071 | let identity := 1072 | addmod( 1073 | mload(QC_EVAL_LOC), addmod(w4q3, addmod(w3q3, addmod(w2q2, addmod(w1q1, w1w2qm, p), p), p), p), p 1074 | ) 1075 | 1076 | // if q_arith == 3 we evaluate an additional mini addition gate (on top of the regular one), where: 1077 | // w_1 + w_4 - w_1_omega + q_m = 0 1078 | // we use this gate to save an addition gate when adding or subtracting non-native field elements 1079 | // α * (q_arith - 2) * (w_1 + w_4 - w_1_omega + q_m) 1080 | let extra_small_addition_gate_identity := 1081 | mulmod( 1082 | mload(C_ALPHA_LOC), 1083 | mulmod( 1084 | addmod(mload(QARITH_EVAL_LOC), sub(p, 2), p), 1085 | addmod( 1086 | mload(QM_EVAL_LOC), 1087 | addmod( 1088 | sub(p, mload(W1_OMEGA_EVAL_LOC)), addmod(mload(W1_EVAL_LOC), mload(W4_EVAL_LOC), p), p 1089 | ), 1090 | p 1091 | ), 1092 | p 1093 | ), 1094 | p 1095 | ) 1096 | 1097 | // if q_arith == 2 OR q_arith == 3 we add the 4th wire of the NEXT gate into the arithmetic identity 1098 | // N.B. if q_arith > 2, this wire value will be scaled by (q_arith - 1) relative to the other gate wires! 1099 | // alpha_base * q_arith * (identity + (q_arith - 1) * (w_4_omega + extra_small_addition_gate_identity)) 1100 | mstore( 1101 | ARITHMETIC_IDENTITY, 1102 | mulmod( 1103 | mload(C_ALPHA_BASE_LOC), 1104 | mulmod( 1105 | mload(QARITH_EVAL_LOC), 1106 | addmod( 1107 | identity, 1108 | mulmod( 1109 | addmod(mload(QARITH_EVAL_LOC), sub(p, 1), p), 1110 | addmod(mload(W4_OMEGA_EVAL_LOC), extra_small_addition_gate_identity, p), 1111 | p 1112 | ), 1113 | p 1114 | ), 1115 | p 1116 | ), 1117 | p 1118 | ) 1119 | ) 1120 | 1121 | // update alpha 1122 | mstore(C_ALPHA_BASE_LOC, mulmod(mload(C_ALPHA_BASE_LOC), mload(C_ALPHA_SQR_LOC), p)) 1123 | } 1124 | 1125 | /** 1126 | * COMPUTE GENPERMSORT WIDGET EVALUATION 1127 | */ 1128 | { 1129 | /** 1130 | * D1 = (w2 - w1) 1131 | * D2 = (w3 - w2) 1132 | * D3 = (w4 - w3) 1133 | * D4 = (w1_omega - w4) 1134 | * 1135 | * α_a = alpha_base 1136 | * α_b = alpha_base * α 1137 | * α_c = alpha_base * α^2 1138 | * α_d = alpha_base * α^3 1139 | * 1140 | * range_accumulator = ( 1141 | * D1(D1 - 1)(D1 - 2)(D1 - 3).α_a + 1142 | * D2(D2 - 1)(D2 - 2)(D2 - 3).α_b + 1143 | * D3(D3 - 1)(D3 - 2)(D3 - 3).α_c + 1144 | * D4(D4 - 1)(D4 - 2)(D4 - 3).α_d + 1145 | * ) . q_sort 1146 | */ 1147 | let minus_two := sub(p, 2) 1148 | let minus_three := sub(p, 3) 1149 | let d1 := addmod(mload(W2_EVAL_LOC), sub(p, mload(W1_EVAL_LOC)), p) 1150 | let d2 := addmod(mload(W3_EVAL_LOC), sub(p, mload(W2_EVAL_LOC)), p) 1151 | let d3 := addmod(mload(W4_EVAL_LOC), sub(p, mload(W3_EVAL_LOC)), p) 1152 | let d4 := addmod(mload(W1_OMEGA_EVAL_LOC), sub(p, mload(W4_EVAL_LOC)), p) 1153 | 1154 | let range_accumulator := 1155 | mulmod( 1156 | mulmod( 1157 | mulmod(addmod(mulmod(d1, d1, p), sub(p, d1), p), addmod(d1, minus_two, p), p), 1158 | addmod(d1, minus_three, p), 1159 | p 1160 | ), 1161 | mload(C_ALPHA_BASE_LOC), 1162 | p 1163 | ) 1164 | range_accumulator := 1165 | addmod( 1166 | range_accumulator, 1167 | mulmod( 1168 | mulmod( 1169 | mulmod(addmod(mulmod(d2, d2, p), sub(p, d2), p), addmod(d2, minus_two, p), p), 1170 | addmod(d2, minus_three, p), 1171 | p 1172 | ), 1173 | mulmod(mload(C_ALPHA_BASE_LOC), mload(C_ALPHA_LOC), p), 1174 | p 1175 | ), 1176 | p 1177 | ) 1178 | range_accumulator := 1179 | addmod( 1180 | range_accumulator, 1181 | mulmod( 1182 | mulmod( 1183 | mulmod(addmod(mulmod(d3, d3, p), sub(p, d3), p), addmod(d3, minus_two, p), p), 1184 | addmod(d3, minus_three, p), 1185 | p 1186 | ), 1187 | mulmod(mload(C_ALPHA_BASE_LOC), mload(C_ALPHA_SQR_LOC), p), 1188 | p 1189 | ), 1190 | p 1191 | ) 1192 | range_accumulator := 1193 | addmod( 1194 | range_accumulator, 1195 | mulmod( 1196 | mulmod( 1197 | mulmod(addmod(mulmod(d4, d4, p), sub(p, d4), p), addmod(d4, minus_two, p), p), 1198 | addmod(d4, minus_three, p), 1199 | p 1200 | ), 1201 | mulmod(mload(C_ALPHA_BASE_LOC), mload(C_ALPHA_CUBE_LOC), p), 1202 | p 1203 | ), 1204 | p 1205 | ) 1206 | range_accumulator := mulmod(range_accumulator, mload(QSORT_EVAL_LOC), p) 1207 | 1208 | mstore(SORT_IDENTITY, range_accumulator) 1209 | 1210 | // update alpha 1211 | mstore(C_ALPHA_BASE_LOC, mulmod(mload(C_ALPHA_BASE_LOC), mload(C_ALPHA_QUAD_LOC), p)) 1212 | } 1213 | 1214 | /** 1215 | * COMPUTE ELLIPTIC WIDGET EVALUATION 1216 | */ 1217 | { 1218 | /** 1219 | * endo_term = (-x_2) * x_1 * (x_3 * 2 + x_1) * q_beta 1220 | * endo_sqr_term = x_2^2 1221 | * endo_sqr_term *= (x_3 - x_1) 1222 | * endo_sqr_term *= q_beta^2 1223 | * leftovers = x_2^2 1224 | * leftovers *= x_2 1225 | * leftovers += x_1^2 * (x_3 + x_1) @follow-up Invalid comment in BB widget 1226 | * leftovers -= (y_2^2 + y_1^2) 1227 | * sign_term = y_2 * y_1 1228 | * sign_term += sign_term 1229 | * sign_term *= q_sign 1230 | */ 1231 | // q_elliptic * (x3 + x2 + x1)(x2 - x1)(x2 - x1) - y2^2 - y1^2 + 2(y2y1)*q_sign = 0 1232 | let x_diff := addmod(mload(X2_EVAL_LOC), sub(p, mload(X1_EVAL_LOC)), p) 1233 | let y2_sqr := mulmod(mload(Y2_EVAL_LOC), mload(Y2_EVAL_LOC), p) 1234 | let y1_sqr := mulmod(mload(Y1_EVAL_LOC), mload(Y1_EVAL_LOC), p) 1235 | let y1y2 := mulmod(mulmod(mload(Y1_EVAL_LOC), mload(Y2_EVAL_LOC), p), mload(QSIGN_LOC), p) 1236 | 1237 | let x_add_identity := 1238 | addmod( 1239 | mulmod( 1240 | addmod(mload(X3_EVAL_LOC), addmod(mload(X2_EVAL_LOC), mload(X1_EVAL_LOC), p), p), 1241 | mulmod(x_diff, x_diff, p), 1242 | p 1243 | ), 1244 | addmod( 1245 | sub( 1246 | p, 1247 | addmod(y2_sqr, y1_sqr, p) 1248 | ), 1249 | addmod(y1y2, y1y2, p), 1250 | p 1251 | ), 1252 | p 1253 | ) 1254 | x_add_identity := 1255 | mulmod( 1256 | mulmod( 1257 | x_add_identity, 1258 | addmod( 1259 | 1, 1260 | sub(p, mload(QM_EVAL_LOC)), 1261 | p 1262 | ), 1263 | p 1264 | ), 1265 | mload(C_ALPHA_BASE_LOC), 1266 | p 1267 | ) 1268 | 1269 | // q_elliptic * (x3 + x2 + x1)(x2 - x1)(x2 - x1) - y2^2 - y1^2 + 2(y2y1)*q_sign = 0 1270 | let y1_plus_y3 := addmod( 1271 | mload(Y1_EVAL_LOC), 1272 | mload(Y3_EVAL_LOC), 1273 | p 1274 | ) 1275 | let y_diff := addmod(mulmod(mload(Y2_EVAL_LOC), mload(QSIGN_LOC), p), sub(p, mload(Y1_EVAL_LOC)), p) 1276 | let y_add_identity := 1277 | addmod( 1278 | mulmod(y1_plus_y3, x_diff, p), 1279 | mulmod(addmod(mload(X3_EVAL_LOC), sub(p, mload(X1_EVAL_LOC)), p), y_diff, p), 1280 | p 1281 | ) 1282 | y_add_identity := 1283 | mulmod( 1284 | mulmod(y_add_identity, addmod(1, sub(p, mload(QM_EVAL_LOC)), p), p), 1285 | mulmod(mload(C_ALPHA_BASE_LOC), mload(C_ALPHA_LOC), p), 1286 | p 1287 | ) 1288 | 1289 | // ELLIPTIC_IDENTITY = (x_identity + y_identity) * Q_ELLIPTIC_EVAL 1290 | mstore( 1291 | ELLIPTIC_IDENTITY, mulmod(addmod(x_add_identity, y_add_identity, p), mload(QELLIPTIC_EVAL_LOC), p) 1292 | ) 1293 | } 1294 | { 1295 | /** 1296 | * x_pow_4 = (y_1_sqr - curve_b) * x_1; 1297 | * y_1_sqr_mul_4 = y_1_sqr + y_1_sqr; 1298 | * y_1_sqr_mul_4 += y_1_sqr_mul_4; 1299 | * x_1_pow_4_mul_9 = x_pow_4; 1300 | * x_1_pow_4_mul_9 += x_1_pow_4_mul_9; 1301 | * x_1_pow_4_mul_9 += x_1_pow_4_mul_9; 1302 | * x_1_pow_4_mul_9 += x_1_pow_4_mul_9; 1303 | * x_1_pow_4_mul_9 += x_pow_4; 1304 | * x_1_sqr_mul_3 = x_1_sqr + x_1_sqr + x_1_sqr; 1305 | * x_double_identity = (x_3 + x_1 + x_1) * y_1_sqr_mul_4 - x_1_pow_4_mul_9; 1306 | * y_double_identity = x_1_sqr_mul_3 * (x_1 - x_3) - (y_1 + y_1) * (y_1 + y_3); 1307 | */ 1308 | // (x3 + x1 + x1) (4y1*y1) - 9 * x1 * x1 * x1 * x1 = 0 1309 | let x1_sqr := mulmod(mload(X1_EVAL_LOC), mload(X1_EVAL_LOC), p) 1310 | let y1_sqr := mulmod(mload(Y1_EVAL_LOC), mload(Y1_EVAL_LOC), p) 1311 | let x_pow_4 := mulmod(addmod(y1_sqr, GRUMPKIN_CURVE_B_PARAMETER_NEGATED, p), mload(X1_EVAL_LOC), p) 1312 | let y1_sqr_mul_4 := mulmod(y1_sqr, 4, p) 1313 | let x1_pow_4_mul_9 := mulmod(x_pow_4, 9, p) 1314 | let x1_sqr_mul_3 := mulmod(x1_sqr, 3, p) 1315 | let x_double_identity := 1316 | addmod( 1317 | mulmod( 1318 | addmod(mload(X3_EVAL_LOC), addmod(mload(X1_EVAL_LOC), mload(X1_EVAL_LOC), p), p), 1319 | y1_sqr_mul_4, 1320 | p 1321 | ), 1322 | sub(p, x1_pow_4_mul_9), 1323 | p 1324 | ) 1325 | // (y1 + y1) (2y1) - (3 * x1 * x1)(x1 - x3) = 0 1326 | let y_double_identity := 1327 | addmod( 1328 | mulmod(x1_sqr_mul_3, addmod(mload(X1_EVAL_LOC), sub(p, mload(X3_EVAL_LOC)), p), p), 1329 | sub( 1330 | p, 1331 | mulmod( 1332 | addmod(mload(Y1_EVAL_LOC), mload(Y1_EVAL_LOC), p), 1333 | addmod(mload(Y1_EVAL_LOC), mload(Y3_EVAL_LOC), p), 1334 | p 1335 | ) 1336 | ), 1337 | p 1338 | ) 1339 | x_double_identity := mulmod(x_double_identity, mload(C_ALPHA_BASE_LOC), p) 1340 | y_double_identity := 1341 | mulmod(y_double_identity, mulmod(mload(C_ALPHA_BASE_LOC), mload(C_ALPHA_LOC), p), p) 1342 | x_double_identity := mulmod(x_double_identity, mload(QM_EVAL_LOC), p) 1343 | y_double_identity := mulmod(y_double_identity, mload(QM_EVAL_LOC), p) 1344 | // ELLIPTIC_IDENTITY += (x_double_identity + y_double_identity) * Q_DOUBLE_EVAL 1345 | mstore( 1346 | ELLIPTIC_IDENTITY, 1347 | addmod( 1348 | mload(ELLIPTIC_IDENTITY), 1349 | mulmod(addmod(x_double_identity, y_double_identity, p), mload(QELLIPTIC_EVAL_LOC), p), 1350 | p 1351 | ) 1352 | ) 1353 | 1354 | // update alpha 1355 | mstore(C_ALPHA_BASE_LOC, mulmod(mload(C_ALPHA_BASE_LOC), mload(C_ALPHA_QUAD_LOC), p)) 1356 | } 1357 | 1358 | /** 1359 | * COMPUTE AUXILIARY WIDGET EVALUATION 1360 | */ 1361 | { 1362 | { 1363 | /** 1364 | * Non native field arithmetic gate 2 1365 | * _ _ 1366 | * / _ _ _ 14 \ 1367 | * q_2 . q_4 | (w_1 . w_2) + (w_1 . w_2) + (w_1 . w_4 + w_2 . w_3 - w_3) . 2 - w_3 - w_4 | 1368 | * \_ _/ 1369 | * 1370 | * limb_subproduct = w_1 . w_2_omega + w_1_omega . w_2 1371 | * non_native_field_gate_2 = w_1 * w_4 + w_4 * w_3 - w_3_omega 1372 | * non_native_field_gate_2 = non_native_field_gate_2 * limb_size 1373 | * non_native_field_gate_2 -= w_4_omega 1374 | * non_native_field_gate_2 += limb_subproduct 1375 | * non_native_field_gate_2 *= q_4 1376 | * limb_subproduct *= limb_size 1377 | * limb_subproduct += w_1_omega * w_2_omega 1378 | * non_native_field_gate_1 = (limb_subproduct + w_3 + w_4) * q_3 1379 | * non_native_field_gate_3 = (limb_subproduct + w_4 - (w_3_omega + w_4_omega)) * q_m 1380 | * non_native_field_identity = (non_native_field_gate_1 + non_native_field_gate_2 + non_native_field_gate_3) * q_2 1381 | */ 1382 | 1383 | let limb_subproduct := 1384 | addmod( 1385 | mulmod(mload(W1_EVAL_LOC), mload(W2_OMEGA_EVAL_LOC), p), 1386 | mulmod(mload(W1_OMEGA_EVAL_LOC), mload(W2_EVAL_LOC), p), 1387 | p 1388 | ) 1389 | 1390 | let non_native_field_gate_2 := 1391 | addmod( 1392 | addmod( 1393 | mulmod(mload(W1_EVAL_LOC), mload(W4_EVAL_LOC), p), 1394 | mulmod(mload(W2_EVAL_LOC), mload(W3_EVAL_LOC), p), 1395 | p 1396 | ), 1397 | sub(p, mload(W3_OMEGA_EVAL_LOC)), 1398 | p 1399 | ) 1400 | non_native_field_gate_2 := mulmod(non_native_field_gate_2, LIMB_SIZE, p) 1401 | non_native_field_gate_2 := addmod(non_native_field_gate_2, sub(p, mload(W4_OMEGA_EVAL_LOC)), p) 1402 | non_native_field_gate_2 := addmod(non_native_field_gate_2, limb_subproduct, p) 1403 | non_native_field_gate_2 := mulmod(non_native_field_gate_2, mload(Q4_EVAL_LOC), p) 1404 | limb_subproduct := mulmod(limb_subproduct, LIMB_SIZE, p) 1405 | limb_subproduct := 1406 | addmod(limb_subproduct, mulmod(mload(W1_OMEGA_EVAL_LOC), mload(W2_OMEGA_EVAL_LOC), p), p) 1407 | let non_native_field_gate_1 := 1408 | mulmod( 1409 | addmod(limb_subproduct, sub(p, addmod(mload(W3_EVAL_LOC), mload(W4_EVAL_LOC), p)), p), 1410 | mload(Q3_EVAL_LOC), 1411 | p 1412 | ) 1413 | let non_native_field_gate_3 := 1414 | mulmod( 1415 | addmod( 1416 | addmod(limb_subproduct, mload(W4_EVAL_LOC), p), 1417 | sub(p, addmod(mload(W3_OMEGA_EVAL_LOC), mload(W4_OMEGA_EVAL_LOC), p)), 1418 | p 1419 | ), 1420 | mload(QM_EVAL_LOC), 1421 | p 1422 | ) 1423 | let non_native_field_identity := 1424 | mulmod( 1425 | addmod(addmod(non_native_field_gate_1, non_native_field_gate_2, p), non_native_field_gate_3, p), 1426 | mload(Q2_EVAL_LOC), 1427 | p 1428 | ) 1429 | 1430 | mstore(AUX_NON_NATIVE_FIELD_EVALUATION, non_native_field_identity) 1431 | } 1432 | 1433 | { 1434 | /** 1435 | * limb_accumulator_1 = w_2_omega; 1436 | * limb_accumulator_1 *= SUBLIMB_SHIFT; 1437 | * limb_accumulator_1 += w_1_omega; 1438 | * limb_accumulator_1 *= SUBLIMB_SHIFT; 1439 | * limb_accumulator_1 += w_3; 1440 | * limb_accumulator_1 *= SUBLIMB_SHIFT; 1441 | * limb_accumulator_1 += w_2; 1442 | * limb_accumulator_1 *= SUBLIMB_SHIFT; 1443 | * limb_accumulator_1 += w_1; 1444 | * limb_accumulator_1 -= w_4; 1445 | * limb_accumulator_1 *= q_4; 1446 | */ 1447 | let limb_accumulator_1 := mulmod(mload(W2_OMEGA_EVAL_LOC), SUBLIMB_SHIFT, p) 1448 | limb_accumulator_1 := addmod(limb_accumulator_1, mload(W1_OMEGA_EVAL_LOC), p) 1449 | limb_accumulator_1 := mulmod(limb_accumulator_1, SUBLIMB_SHIFT, p) 1450 | limb_accumulator_1 := addmod(limb_accumulator_1, mload(W3_EVAL_LOC), p) 1451 | limb_accumulator_1 := mulmod(limb_accumulator_1, SUBLIMB_SHIFT, p) 1452 | limb_accumulator_1 := addmod(limb_accumulator_1, mload(W2_EVAL_LOC), p) 1453 | limb_accumulator_1 := mulmod(limb_accumulator_1, SUBLIMB_SHIFT, p) 1454 | limb_accumulator_1 := addmod(limb_accumulator_1, mload(W1_EVAL_LOC), p) 1455 | limb_accumulator_1 := addmod(limb_accumulator_1, sub(p, mload(W4_EVAL_LOC)), p) 1456 | limb_accumulator_1 := mulmod(limb_accumulator_1, mload(Q4_EVAL_LOC), p) 1457 | 1458 | /** 1459 | * limb_accumulator_2 = w_3_omega; 1460 | * limb_accumulator_2 *= SUBLIMB_SHIFT; 1461 | * limb_accumulator_2 += w_2_omega; 1462 | * limb_accumulator_2 *= SUBLIMB_SHIFT; 1463 | * limb_accumulator_2 += w_1_omega; 1464 | * limb_accumulator_2 *= SUBLIMB_SHIFT; 1465 | * limb_accumulator_2 += w_4; 1466 | * limb_accumulator_2 *= SUBLIMB_SHIFT; 1467 | * limb_accumulator_2 += w_3; 1468 | * limb_accumulator_2 -= w_4_omega; 1469 | * limb_accumulator_2 *= q_m; 1470 | */ 1471 | let limb_accumulator_2 := mulmod(mload(W3_OMEGA_EVAL_LOC), SUBLIMB_SHIFT, p) 1472 | limb_accumulator_2 := addmod(limb_accumulator_2, mload(W2_OMEGA_EVAL_LOC), p) 1473 | limb_accumulator_2 := mulmod(limb_accumulator_2, SUBLIMB_SHIFT, p) 1474 | limb_accumulator_2 := addmod(limb_accumulator_2, mload(W1_OMEGA_EVAL_LOC), p) 1475 | limb_accumulator_2 := mulmod(limb_accumulator_2, SUBLIMB_SHIFT, p) 1476 | limb_accumulator_2 := addmod(limb_accumulator_2, mload(W4_EVAL_LOC), p) 1477 | limb_accumulator_2 := mulmod(limb_accumulator_2, SUBLIMB_SHIFT, p) 1478 | limb_accumulator_2 := addmod(limb_accumulator_2, mload(W3_EVAL_LOC), p) 1479 | limb_accumulator_2 := addmod(limb_accumulator_2, sub(p, mload(W4_OMEGA_EVAL_LOC)), p) 1480 | limb_accumulator_2 := mulmod(limb_accumulator_2, mload(QM_EVAL_LOC), p) 1481 | 1482 | mstore( 1483 | AUX_LIMB_ACCUMULATOR_EVALUATION, 1484 | mulmod(addmod(limb_accumulator_1, limb_accumulator_2, p), mload(Q3_EVAL_LOC), p) 1485 | ) 1486 | } 1487 | 1488 | { 1489 | /** 1490 | * memory_record_check = w_3; 1491 | * memory_record_check *= eta; 1492 | * memory_record_check += w_2; 1493 | * memory_record_check *= eta; 1494 | * memory_record_check += w_1; 1495 | * memory_record_check *= eta; 1496 | * memory_record_check += q_c; 1497 | * 1498 | * partial_record_check = memory_record_check; 1499 | * 1500 | * memory_record_check -= w_4; 1501 | */ 1502 | 1503 | let memory_record_check := mulmod(mload(W3_EVAL_LOC), mload(C_ETA_LOC), p) 1504 | memory_record_check := addmod(memory_record_check, mload(W2_EVAL_LOC), p) 1505 | memory_record_check := mulmod(memory_record_check, mload(C_ETA_LOC), p) 1506 | memory_record_check := addmod(memory_record_check, mload(W1_EVAL_LOC), p) 1507 | memory_record_check := mulmod(memory_record_check, mload(C_ETA_LOC), p) 1508 | memory_record_check := addmod(memory_record_check, mload(QC_EVAL_LOC), p) 1509 | 1510 | let partial_record_check := memory_record_check 1511 | memory_record_check := addmod(memory_record_check, sub(p, mload(W4_EVAL_LOC)), p) 1512 | 1513 | mstore(AUX_MEMORY_EVALUATION, memory_record_check) 1514 | 1515 | // index_delta = w_1_omega - w_1 1516 | let index_delta := addmod(mload(W1_OMEGA_EVAL_LOC), sub(p, mload(W1_EVAL_LOC)), p) 1517 | // record_delta = w_4_omega - w_4 1518 | let record_delta := addmod(mload(W4_OMEGA_EVAL_LOC), sub(p, mload(W4_EVAL_LOC)), p) 1519 | // index_is_monotonically_increasing = index_delta * (index_delta - 1) 1520 | let index_is_monotonically_increasing := mulmod(index_delta, addmod(index_delta, sub(p, 1), p), p) 1521 | 1522 | // adjacent_values_match_if_adjacent_indices_match = record_delta * (1 - index_delta) 1523 | let adjacent_values_match_if_adjacent_indices_match := 1524 | mulmod(record_delta, addmod(1, sub(p, index_delta), p), p) 1525 | 1526 | // AUX_ROM_CONSISTENCY_EVALUATION = ((adjacent_values_match_if_adjacent_indices_match * alpha) + index_is_monotonically_increasing) * alpha + partial_record_check 1527 | mstore( 1528 | AUX_ROM_CONSISTENCY_EVALUATION, 1529 | addmod( 1530 | mulmod( 1531 | addmod( 1532 | mulmod(adjacent_values_match_if_adjacent_indices_match, mload(C_ALPHA_LOC), p), 1533 | index_is_monotonically_increasing, 1534 | p 1535 | ), 1536 | mload(C_ALPHA_LOC), 1537 | p 1538 | ), 1539 | memory_record_check, 1540 | p 1541 | ) 1542 | ) 1543 | 1544 | { 1545 | /** 1546 | * next_gate_access_type = w_3_omega; 1547 | * next_gate_access_type *= eta; 1548 | * next_gate_access_type += w_2_omega; 1549 | * next_gate_access_type *= eta; 1550 | * next_gate_access_type += w_1_omega; 1551 | * next_gate_access_type *= eta; 1552 | * next_gate_access_type = w_4_omega - next_gate_access_type; 1553 | */ 1554 | let next_gate_access_type := mulmod(mload(W3_OMEGA_EVAL_LOC), mload(C_ETA_LOC), p) 1555 | next_gate_access_type := addmod(next_gate_access_type, mload(W2_OMEGA_EVAL_LOC), p) 1556 | next_gate_access_type := mulmod(next_gate_access_type, mload(C_ETA_LOC), p) 1557 | next_gate_access_type := addmod(next_gate_access_type, mload(W1_OMEGA_EVAL_LOC), p) 1558 | next_gate_access_type := mulmod(next_gate_access_type, mload(C_ETA_LOC), p) 1559 | next_gate_access_type := addmod(mload(W4_OMEGA_EVAL_LOC), sub(p, next_gate_access_type), p) 1560 | 1561 | // value_delta = w_3_omega - w_3 1562 | let value_delta := addmod(mload(W3_OMEGA_EVAL_LOC), sub(p, mload(W3_EVAL_LOC)), p) 1563 | // adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation = (1 - index_delta) * value_delta * (1 - next_gate_access_type); 1564 | 1565 | let adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation := 1566 | mulmod( 1567 | addmod(1, sub(p, index_delta), p), 1568 | mulmod(value_delta, addmod(1, sub(p, next_gate_access_type), p), p), 1569 | p 1570 | ) 1571 | 1572 | // AUX_RAM_CONSISTENCY_EVALUATION 1573 | 1574 | /** 1575 | * access_type = w_4 - partial_record_check 1576 | * access_check = access_type^2 - access_type 1577 | * next_gate_access_type_is_boolean = next_gate_access_type^2 - next_gate_access_type 1578 | * RAM_consistency_check_identity = adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation; 1579 | * RAM_consistency_check_identity *= alpha; 1580 | * RAM_consistency_check_identity += index_is_monotonically_increasing; 1581 | * RAM_consistency_check_identity *= alpha; 1582 | * RAM_consistency_check_identity += next_gate_access_type_is_boolean; 1583 | * RAM_consistency_check_identity *= alpha; 1584 | * RAM_consistency_check_identity += access_check; 1585 | */ 1586 | 1587 | let access_type := addmod(mload(W4_EVAL_LOC), sub(p, partial_record_check), p) 1588 | let access_check := mulmod(access_type, addmod(access_type, sub(p, 1), p), p) 1589 | let next_gate_access_type_is_boolean := 1590 | mulmod(next_gate_access_type, addmod(next_gate_access_type, sub(p, 1), p), p) 1591 | let RAM_cci := 1592 | mulmod( 1593 | adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation, 1594 | mload(C_ALPHA_LOC), 1595 | p 1596 | ) 1597 | RAM_cci := addmod(RAM_cci, index_is_monotonically_increasing, p) 1598 | RAM_cci := mulmod(RAM_cci, mload(C_ALPHA_LOC), p) 1599 | RAM_cci := addmod(RAM_cci, next_gate_access_type_is_boolean, p) 1600 | RAM_cci := mulmod(RAM_cci, mload(C_ALPHA_LOC), p) 1601 | RAM_cci := addmod(RAM_cci, access_check, p) 1602 | 1603 | mstore(AUX_RAM_CONSISTENCY_EVALUATION, RAM_cci) 1604 | } 1605 | 1606 | { 1607 | // timestamp_delta = w_2_omega - w_2 1608 | let timestamp_delta := addmod(mload(W2_OMEGA_EVAL_LOC), sub(p, mload(W2_EVAL_LOC)), p) 1609 | 1610 | // RAM_timestamp_check_identity = (1 - index_delta) * timestamp_delta - w_3 1611 | let RAM_timestamp_check_identity := 1612 | addmod( 1613 | mulmod(timestamp_delta, addmod(1, sub(p, index_delta), p), p), sub(p, mload(W3_EVAL_LOC)), p 1614 | ) 1615 | 1616 | /** 1617 | * memory_identity = ROM_consistency_check_identity * q_2; 1618 | * memory_identity += RAM_timestamp_check_identity * q_4; 1619 | * memory_identity += memory_record_check * q_m; 1620 | * memory_identity *= q_1; 1621 | * memory_identity += (RAM_consistency_check_identity * q_arith); 1622 | * 1623 | * auxiliary_identity = memory_identity + non_native_field_identity + limb_accumulator_identity; 1624 | * auxiliary_identity *= q_aux; 1625 | * auxiliary_identity *= alpha_base; 1626 | */ 1627 | let memory_identity := mulmod(mload(AUX_ROM_CONSISTENCY_EVALUATION), mload(Q2_EVAL_LOC), p) 1628 | memory_identity := 1629 | addmod(memory_identity, mulmod(RAM_timestamp_check_identity, mload(Q4_EVAL_LOC), p), p) 1630 | memory_identity := 1631 | addmod(memory_identity, mulmod(mload(AUX_MEMORY_EVALUATION), mload(QM_EVAL_LOC), p), p) 1632 | memory_identity := mulmod(memory_identity, mload(Q1_EVAL_LOC), p) 1633 | memory_identity := 1634 | addmod( 1635 | memory_identity, mulmod(mload(AUX_RAM_CONSISTENCY_EVALUATION), mload(QARITH_EVAL_LOC), p), p 1636 | ) 1637 | 1638 | let auxiliary_identity := addmod(memory_identity, mload(AUX_NON_NATIVE_FIELD_EVALUATION), p) 1639 | auxiliary_identity := addmod(auxiliary_identity, mload(AUX_LIMB_ACCUMULATOR_EVALUATION), p) 1640 | auxiliary_identity := mulmod(auxiliary_identity, mload(QAUX_EVAL_LOC), p) 1641 | auxiliary_identity := mulmod(auxiliary_identity, mload(C_ALPHA_BASE_LOC), p) 1642 | 1643 | mstore(AUX_IDENTITY, auxiliary_identity) 1644 | 1645 | // update alpha 1646 | mstore(C_ALPHA_BASE_LOC, mulmod(mload(C_ALPHA_BASE_LOC), mload(C_ALPHA_CUBE_LOC), p)) 1647 | } 1648 | } 1649 | } 1650 | 1651 | { 1652 | /** 1653 | * quotient = ARITHMETIC_IDENTITY 1654 | * quotient += PERMUTATION_IDENTITY 1655 | * quotient += PLOOKUP_IDENTITY 1656 | * quotient += SORT_IDENTITY 1657 | * quotient += ELLIPTIC_IDENTITY 1658 | * quotient += AUX_IDENTITY 1659 | * quotient *= ZERO_POLY_INVERSE 1660 | */ 1661 | mstore( 1662 | QUOTIENT_EVAL_LOC, 1663 | mulmod( 1664 | addmod( 1665 | addmod( 1666 | addmod( 1667 | addmod( 1668 | addmod(mload(PERMUTATION_IDENTITY), mload(PLOOKUP_IDENTITY), p), 1669 | mload(ARITHMETIC_IDENTITY), 1670 | p 1671 | ), 1672 | mload(SORT_IDENTITY), 1673 | p 1674 | ), 1675 | mload(ELLIPTIC_IDENTITY), 1676 | p 1677 | ), 1678 | mload(AUX_IDENTITY), 1679 | p 1680 | ), 1681 | mload(ZERO_POLY_INVERSE_LOC), 1682 | p 1683 | ) 1684 | ) 1685 | } 1686 | 1687 | /** 1688 | * GENERATE NU AND SEPARATOR CHALLENGES 1689 | */ 1690 | { 1691 | let current_challenge := mload(C_CURRENT_LOC) 1692 | // get a calldata pointer that points to the start of the data we want to copy 1693 | let calldata_ptr := add(calldataload(0x04), 0x24) 1694 | 1695 | calldata_ptr := add(calldata_ptr, NU_CALLDATA_SKIP_LENGTH) 1696 | 1697 | mstore(NU_CHALLENGE_INPUT_LOC_A, current_challenge) 1698 | mstore(NU_CHALLENGE_INPUT_LOC_B, mload(QUOTIENT_EVAL_LOC)) 1699 | calldatacopy(NU_CHALLENGE_INPUT_LOC_C, calldata_ptr, NU_INPUT_LENGTH) 1700 | 1701 | // hash length = (0x20 + num field elements), we include the previous challenge in the hash 1702 | let challenge := keccak256(NU_CHALLENGE_INPUT_LOC_A, add(NU_INPUT_LENGTH, 0x40)) 1703 | 1704 | mstore(C_V0_LOC, mod(challenge, p)) 1705 | // We need THIRTY-ONE independent nu challenges! 1706 | mstore(0x00, challenge) 1707 | mstore8(0x20, 0x01) 1708 | mstore(C_V1_LOC, mod(keccak256(0x00, 0x21), p)) 1709 | mstore8(0x20, 0x02) 1710 | mstore(C_V2_LOC, mod(keccak256(0x00, 0x21), p)) 1711 | mstore8(0x20, 0x03) 1712 | mstore(C_V3_LOC, mod(keccak256(0x00, 0x21), p)) 1713 | mstore8(0x20, 0x04) 1714 | mstore(C_V4_LOC, mod(keccak256(0x00, 0x21), p)) 1715 | mstore8(0x20, 0x05) 1716 | mstore(C_V5_LOC, mod(keccak256(0x00, 0x21), p)) 1717 | mstore8(0x20, 0x06) 1718 | mstore(C_V6_LOC, mod(keccak256(0x00, 0x21), p)) 1719 | mstore8(0x20, 0x07) 1720 | mstore(C_V7_LOC, mod(keccak256(0x00, 0x21), p)) 1721 | mstore8(0x20, 0x08) 1722 | mstore(C_V8_LOC, mod(keccak256(0x00, 0x21), p)) 1723 | mstore8(0x20, 0x09) 1724 | mstore(C_V9_LOC, mod(keccak256(0x00, 0x21), p)) 1725 | mstore8(0x20, 0x0a) 1726 | mstore(C_V10_LOC, mod(keccak256(0x00, 0x21), p)) 1727 | mstore8(0x20, 0x0b) 1728 | mstore(C_V11_LOC, mod(keccak256(0x00, 0x21), p)) 1729 | mstore8(0x20, 0x0c) 1730 | mstore(C_V12_LOC, mod(keccak256(0x00, 0x21), p)) 1731 | mstore8(0x20, 0x0d) 1732 | mstore(C_V13_LOC, mod(keccak256(0x00, 0x21), p)) 1733 | mstore8(0x20, 0x0e) 1734 | mstore(C_V14_LOC, mod(keccak256(0x00, 0x21), p)) 1735 | mstore8(0x20, 0x0f) 1736 | mstore(C_V15_LOC, mod(keccak256(0x00, 0x21), p)) 1737 | mstore8(0x20, 0x10) 1738 | mstore(C_V16_LOC, mod(keccak256(0x00, 0x21), p)) 1739 | mstore8(0x20, 0x11) 1740 | mstore(C_V17_LOC, mod(keccak256(0x00, 0x21), p)) 1741 | mstore8(0x20, 0x12) 1742 | mstore(C_V18_LOC, mod(keccak256(0x00, 0x21), p)) 1743 | mstore8(0x20, 0x13) 1744 | mstore(C_V19_LOC, mod(keccak256(0x00, 0x21), p)) 1745 | mstore8(0x20, 0x14) 1746 | mstore(C_V20_LOC, mod(keccak256(0x00, 0x21), p)) 1747 | mstore8(0x20, 0x15) 1748 | mstore(C_V21_LOC, mod(keccak256(0x00, 0x21), p)) 1749 | mstore8(0x20, 0x16) 1750 | mstore(C_V22_LOC, mod(keccak256(0x00, 0x21), p)) 1751 | mstore8(0x20, 0x17) 1752 | mstore(C_V23_LOC, mod(keccak256(0x00, 0x21), p)) 1753 | mstore8(0x20, 0x18) 1754 | mstore(C_V24_LOC, mod(keccak256(0x00, 0x21), p)) 1755 | mstore8(0x20, 0x19) 1756 | mstore(C_V25_LOC, mod(keccak256(0x00, 0x21), p)) 1757 | mstore8(0x20, 0x1a) 1758 | mstore(C_V26_LOC, mod(keccak256(0x00, 0x21), p)) 1759 | mstore8(0x20, 0x1b) 1760 | mstore(C_V27_LOC, mod(keccak256(0x00, 0x21), p)) 1761 | mstore8(0x20, 0x1c) 1762 | mstore(C_V28_LOC, mod(keccak256(0x00, 0x21), p)) 1763 | mstore8(0x20, 0x1d) 1764 | mstore(C_V29_LOC, mod(keccak256(0x00, 0x21), p)) 1765 | 1766 | // @follow-up - Why are both v29 and v30 using appending 0x1d to the prior challenge and hashing, should it not change? 1767 | mstore8(0x20, 0x1d) 1768 | challenge := keccak256(0x00, 0x21) 1769 | mstore(C_V30_LOC, mod(challenge, p)) 1770 | 1771 | // separator 1772 | mstore(0x00, challenge) 1773 | mstore(0x20, mload(PI_Z_Y_LOC)) 1774 | mstore(0x40, mload(PI_Z_X_LOC)) 1775 | mstore(0x60, mload(PI_Z_OMEGA_Y_LOC)) 1776 | mstore(0x80, mload(PI_Z_OMEGA_X_LOC)) 1777 | 1778 | mstore(C_U_LOC, mod(keccak256(0x00, 0xa0), p)) 1779 | } 1780 | 1781 | let success := 0 1782 | // VALIDATE T1 1783 | { 1784 | let x := mload(T1_X_LOC) 1785 | let y := mload(T1_Y_LOC) 1786 | let xx := mulmod(x, x, q) 1787 | // validate on curve 1788 | success := eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)) 1789 | mstore(ACCUMULATOR_X_LOC, x) 1790 | mstore(add(ACCUMULATOR_X_LOC, 0x20), y) 1791 | } 1792 | // VALIDATE T2 1793 | { 1794 | let x := mload(T2_X_LOC) // 0x1400 1795 | let y := mload(T2_Y_LOC) // 0x1420 1796 | let xx := mulmod(x, x, q) 1797 | // validate on curve 1798 | success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q))) 1799 | mstore(0x00, x) 1800 | mstore(0x20, y) 1801 | } 1802 | mstore(0x40, mload(ZETA_POW_N_LOC)) 1803 | // accumulator_2 = [T2].zeta^n 1804 | success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40)) 1805 | // accumulator = [T1] + accumulator_2 1806 | success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40)) 1807 | 1808 | // VALIDATE T3 1809 | { 1810 | let x := mload(T3_X_LOC) 1811 | let y := mload(T3_Y_LOC) 1812 | let xx := mulmod(x, x, q) 1813 | // validate on curve 1814 | success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q))) 1815 | mstore(0x00, x) 1816 | mstore(0x20, y) 1817 | } 1818 | mstore(0x40, mulmod(mload(ZETA_POW_N_LOC), mload(ZETA_POW_N_LOC), p)) 1819 | // accumulator_2 = [T3].zeta^{2n} 1820 | success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40)) 1821 | // accumulator = accumulator + accumulator_2 1822 | success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40)) 1823 | 1824 | // VALIDATE T4 1825 | { 1826 | let x := mload(T4_X_LOC) 1827 | let y := mload(T4_Y_LOC) 1828 | let xx := mulmod(x, x, q) 1829 | // validate on curve 1830 | success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q))) 1831 | mstore(0x00, x) 1832 | mstore(0x20, y) 1833 | } 1834 | mstore(0x40, mulmod(mulmod(mload(ZETA_POW_N_LOC), mload(ZETA_POW_N_LOC), p), mload(ZETA_POW_N_LOC), p)) 1835 | // accumulator_2 = [T4].zeta^{3n} 1836 | success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40)) 1837 | // accumulator = accumulator + accumulator_2 1838 | success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40)) 1839 | 1840 | // VALIDATE W1 1841 | { 1842 | let x := mload(W1_X_LOC) 1843 | let y := mload(W1_Y_LOC) 1844 | let xx := mulmod(x, x, q) 1845 | // validate on curve 1846 | success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q))) 1847 | mstore(0x00, x) 1848 | mstore(0x20, y) 1849 | } 1850 | mstore(0x40, mulmod(addmod(mload(C_U_LOC), 0x1, p), mload(C_V0_LOC), p)) 1851 | // accumulator_2 = v0.(u + 1).[W1] 1852 | success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40)) 1853 | // accumulator = accumulator + accumulator_2 1854 | success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40)) 1855 | 1856 | // VALIDATE W2 1857 | { 1858 | let x := mload(W2_X_LOC) 1859 | let y := mload(W2_Y_LOC) 1860 | let xx := mulmod(x, x, q) 1861 | // validate on curve 1862 | success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q))) 1863 | mstore(0x00, x) 1864 | mstore(0x20, y) 1865 | } 1866 | mstore(0x40, mulmod(addmod(mload(C_U_LOC), 0x1, p), mload(C_V1_LOC), p)) 1867 | // accumulator_2 = v1.(u + 1).[W2] 1868 | success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40)) 1869 | // accumulator = accumulator + accumulator_2 1870 | success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40)) 1871 | 1872 | // VALIDATE W3 1873 | { 1874 | let x := mload(W3_X_LOC) 1875 | let y := mload(W3_Y_LOC) 1876 | let xx := mulmod(x, x, q) 1877 | // validate on curve 1878 | success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q))) 1879 | mstore(0x00, x) 1880 | mstore(0x20, y) 1881 | } 1882 | mstore(0x40, mulmod(addmod(mload(C_U_LOC), 0x1, p), mload(C_V2_LOC), p)) 1883 | // accumulator_2 = v2.(u + 1).[W3] 1884 | success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40)) 1885 | // accumulator = accumulator + accumulator_2 1886 | success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40)) 1887 | 1888 | // VALIDATE W4 1889 | { 1890 | let x := mload(W4_X_LOC) 1891 | let y := mload(W4_Y_LOC) 1892 | let xx := mulmod(x, x, q) 1893 | // validate on curve 1894 | success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q))) 1895 | mstore(0x00, x) 1896 | mstore(0x20, y) 1897 | } 1898 | mstore(0x40, mulmod(addmod(mload(C_U_LOC), 0x1, p), mload(C_V3_LOC), p)) 1899 | // accumulator_2 = v3.(u + 1).[W4] 1900 | success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40)) 1901 | // accumulator = accumulator + accumulator_2 1902 | success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40)) 1903 | 1904 | // VALIDATE S 1905 | { 1906 | let x := mload(S_X_LOC) 1907 | let y := mload(S_Y_LOC) 1908 | let xx := mulmod(x, x, q) 1909 | // validate on curve 1910 | success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q))) 1911 | mstore(0x00, x) 1912 | mstore(0x20, y) 1913 | } 1914 | mstore(0x40, mulmod(addmod(mload(C_U_LOC), 0x1, p), mload(C_V4_LOC), p)) 1915 | // accumulator_2 = v4.(u + 1).[S] 1916 | success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40)) 1917 | // accumulator = accumulator + accumulator_2 1918 | success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40)) 1919 | 1920 | // VALIDATE Z 1921 | { 1922 | let x := mload(Z_X_LOC) 1923 | let y := mload(Z_Y_LOC) 1924 | let xx := mulmod(x, x, q) 1925 | // validate on curve 1926 | success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q))) 1927 | mstore(0x00, x) 1928 | mstore(0x20, y) 1929 | } 1930 | mstore(0x40, mulmod(addmod(mload(C_U_LOC), 0x1, p), mload(C_V5_LOC), p)) 1931 | // accumulator_2 = v5.(u + 1).[Z] 1932 | success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40)) 1933 | // accumulator = accumulator + accumulator_2 1934 | success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40)) 1935 | 1936 | // VALIDATE Z_LOOKUP 1937 | { 1938 | let x := mload(Z_LOOKUP_X_LOC) 1939 | let y := mload(Z_LOOKUP_Y_LOC) 1940 | let xx := mulmod(x, x, q) 1941 | // validate on curve 1942 | success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q))) 1943 | mstore(0x00, x) 1944 | mstore(0x20, y) 1945 | } 1946 | mstore(0x40, mulmod(addmod(mload(C_U_LOC), 0x1, p), mload(C_V6_LOC), p)) 1947 | // accumulator_2 = v6.(u + 1).[Z_LOOKUP] 1948 | success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40)) 1949 | // accumulator = accumulator + accumulator_2 1950 | success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40)) 1951 | 1952 | // VALIDATE Q1 1953 | { 1954 | let x := mload(Q1_X_LOC) 1955 | let y := mload(Q1_Y_LOC) 1956 | let xx := mulmod(x, x, q) 1957 | // validate on curve 1958 | success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q))) 1959 | mstore(0x00, x) 1960 | mstore(0x20, y) 1961 | } 1962 | mstore(0x40, mload(C_V7_LOC)) 1963 | // accumulator_2 = v7.[Q1] 1964 | success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40)) 1965 | // accumulator = accumulator + accumulator_2 1966 | success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40)) 1967 | 1968 | // VALIDATE Q2 1969 | { 1970 | let x := mload(Q2_X_LOC) 1971 | let y := mload(Q2_Y_LOC) 1972 | let xx := mulmod(x, x, q) 1973 | // validate on curve 1974 | success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q))) 1975 | mstore(0x00, x) 1976 | mstore(0x20, y) 1977 | } 1978 | mstore(0x40, mload(C_V8_LOC)) 1979 | // accumulator_2 = v8.[Q2] 1980 | success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40)) 1981 | // accumulator = accumulator + accumulator_2 1982 | success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40)) 1983 | 1984 | // VALIDATE Q3 1985 | { 1986 | let x := mload(Q3_X_LOC) 1987 | let y := mload(Q3_Y_LOC) 1988 | let xx := mulmod(x, x, q) 1989 | // validate on curve 1990 | success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q))) 1991 | mstore(0x00, x) 1992 | mstore(0x20, y) 1993 | } 1994 | mstore(0x40, mload(C_V9_LOC)) 1995 | // accumulator_2 = v9.[Q3] 1996 | success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40)) 1997 | // accumulator = accumulator + accumulator_2 1998 | success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40)) 1999 | 2000 | // VALIDATE Q4 2001 | { 2002 | let x := mload(Q4_X_LOC) 2003 | let y := mload(Q4_Y_LOC) 2004 | let xx := mulmod(x, x, q) 2005 | // validate on curve 2006 | success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q))) 2007 | mstore(0x00, x) 2008 | mstore(0x20, y) 2009 | } 2010 | mstore(0x40, mload(C_V10_LOC)) 2011 | // accumulator_2 = v10.[Q4] 2012 | success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40)) 2013 | // accumulator = accumulator + accumulator_2 2014 | success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40)) 2015 | 2016 | // VALIDATE QM 2017 | { 2018 | let x := mload(QM_X_LOC) 2019 | let y := mload(QM_Y_LOC) 2020 | let xx := mulmod(x, x, q) 2021 | // validate on curve 2022 | success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q))) 2023 | mstore(0x00, x) 2024 | mstore(0x20, y) 2025 | } 2026 | mstore(0x40, mload(C_V11_LOC)) 2027 | // accumulator_2 = v11.[Q;] 2028 | success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40)) 2029 | // accumulator = accumulator + accumulator_2 2030 | success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40)) 2031 | 2032 | // VALIDATE QC 2033 | { 2034 | let x := mload(QC_X_LOC) 2035 | let y := mload(QC_Y_LOC) 2036 | let xx := mulmod(x, x, q) 2037 | // validate on curve 2038 | success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q))) 2039 | mstore(0x00, x) 2040 | mstore(0x20, y) 2041 | } 2042 | mstore(0x40, mload(C_V12_LOC)) 2043 | // accumulator_2 = v12.[QC] 2044 | success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40)) 2045 | // accumulator = accumulator + accumulator_2 2046 | success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40)) 2047 | 2048 | // VALIDATE QARITH 2049 | { 2050 | let x := mload(QARITH_X_LOC) 2051 | let y := mload(QARITH_Y_LOC) 2052 | let xx := mulmod(x, x, q) 2053 | // validate on curve 2054 | success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q))) 2055 | mstore(0x00, x) 2056 | mstore(0x20, y) 2057 | } 2058 | mstore(0x40, mload(C_V13_LOC)) 2059 | // accumulator_2 = v13.[QARITH] 2060 | success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40)) 2061 | // accumulator = accumulator + accumulator_2 2062 | success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40)) 2063 | 2064 | // VALIDATE QSORT 2065 | { 2066 | let x := mload(QSORT_X_LOC) 2067 | let y := mload(QSORT_Y_LOC) 2068 | let xx := mulmod(x, x, q) 2069 | // validate on curve 2070 | success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q))) 2071 | mstore(0x00, x) 2072 | mstore(0x20, y) 2073 | } 2074 | mstore(0x40, mload(C_V14_LOC)) 2075 | // accumulator_2 = v14.[QSORT] 2076 | success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40)) 2077 | // accumulator = accumulator + accumulator_2 2078 | success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40)) 2079 | 2080 | // VALIDATE QELLIPTIC 2081 | { 2082 | let x := mload(QELLIPTIC_X_LOC) 2083 | let y := mload(QELLIPTIC_Y_LOC) 2084 | let xx := mulmod(x, x, q) 2085 | // validate on curve 2086 | success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q))) 2087 | mstore(0x00, x) 2088 | mstore(0x20, y) 2089 | } 2090 | mstore(0x40, mload(C_V15_LOC)) 2091 | // accumulator_2 = v15.[QELLIPTIC] 2092 | success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40)) 2093 | // accumulator = accumulator + accumulator_2 2094 | success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40)) 2095 | 2096 | // VALIDATE QAUX 2097 | { 2098 | let x := mload(QAUX_X_LOC) 2099 | let y := mload(QAUX_Y_LOC) 2100 | let xx := mulmod(x, x, q) 2101 | // validate on curve 2102 | success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q))) 2103 | mstore(0x00, x) 2104 | mstore(0x20, y) 2105 | } 2106 | mstore(0x40, mload(C_V16_LOC)) 2107 | // accumulator_2 = v15.[Q_AUX] 2108 | success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40)) 2109 | // accumulator = accumulator + accumulator_2 2110 | success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40)) 2111 | 2112 | // VALIDATE SIGMA1 2113 | { 2114 | let x := mload(SIGMA1_X_LOC) 2115 | let y := mload(SIGMA1_Y_LOC) 2116 | let xx := mulmod(x, x, q) 2117 | // validate on curve 2118 | success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q))) 2119 | mstore(0x00, x) 2120 | mstore(0x20, y) 2121 | } 2122 | mstore(0x40, mload(C_V17_LOC)) 2123 | // accumulator_2 = v17.[sigma1] 2124 | success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40)) 2125 | // accumulator = accumulator + accumulator_2 2126 | success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40)) 2127 | 2128 | // VALIDATE SIGMA2 2129 | { 2130 | let x := mload(SIGMA2_X_LOC) 2131 | let y := mload(SIGMA2_Y_LOC) 2132 | let xx := mulmod(x, x, q) 2133 | // validate on curve 2134 | success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q))) 2135 | mstore(0x00, x) 2136 | mstore(0x20, y) 2137 | } 2138 | mstore(0x40, mload(C_V18_LOC)) 2139 | // accumulator_2 = v18.[sigma2] 2140 | success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40)) 2141 | // accumulator = accumulator + accumulator_2 2142 | success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40)) 2143 | 2144 | // VALIDATE SIGMA3 2145 | { 2146 | let x := mload(SIGMA3_X_LOC) 2147 | let y := mload(SIGMA3_Y_LOC) 2148 | let xx := mulmod(x, x, q) 2149 | // validate on curve 2150 | success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q))) 2151 | mstore(0x00, x) 2152 | mstore(0x20, y) 2153 | } 2154 | mstore(0x40, mload(C_V19_LOC)) 2155 | // accumulator_2 = v19.[sigma3] 2156 | success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40)) 2157 | // accumulator = accumulator + accumulator_2 2158 | success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40)) 2159 | 2160 | // VALIDATE SIGMA4 2161 | { 2162 | let x := mload(SIGMA4_X_LOC) 2163 | let y := mload(SIGMA4_Y_LOC) 2164 | let xx := mulmod(x, x, q) 2165 | // validate on curve 2166 | success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q))) 2167 | mstore(0x00, x) 2168 | mstore(0x20, y) 2169 | } 2170 | mstore(0x40, mload(C_V20_LOC)) 2171 | // accumulator_2 = v20.[sigma4] 2172 | success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40)) 2173 | // accumulator = accumulator + accumulator_2 2174 | success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40)) 2175 | 2176 | // VALIDATE TABLE1 2177 | { 2178 | let x := mload(TABLE1_X_LOC) 2179 | let y := mload(TABLE1_Y_LOC) 2180 | let xx := mulmod(x, x, q) 2181 | // validate on curve 2182 | success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q))) 2183 | mstore(0x00, x) 2184 | mstore(0x20, y) 2185 | } 2186 | mstore(0x40, mulmod(addmod(mload(C_U_LOC), 0x1, p), mload(C_V21_LOC), p)) 2187 | // accumulator_2 = u.[table1] 2188 | success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40)) 2189 | // accumulator = accumulator + accumulator_2 2190 | success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40)) 2191 | 2192 | // VALIDATE TABLE2 2193 | { 2194 | let x := mload(TABLE2_X_LOC) 2195 | let y := mload(TABLE2_Y_LOC) 2196 | let xx := mulmod(x, x, q) 2197 | // validate on curve 2198 | success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q))) 2199 | mstore(0x00, x) 2200 | mstore(0x20, y) 2201 | } 2202 | mstore(0x40, mulmod(addmod(mload(C_U_LOC), 0x1, p), mload(C_V22_LOC), p)) 2203 | // accumulator_2 = u.[table2] 2204 | success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40)) 2205 | // accumulator = accumulator + accumulator_2 2206 | success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40)) 2207 | 2208 | // VALIDATE TABLE3 2209 | { 2210 | let x := mload(TABLE3_X_LOC) 2211 | let y := mload(TABLE3_Y_LOC) 2212 | let xx := mulmod(x, x, q) 2213 | // validate on curve 2214 | success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q))) 2215 | mstore(0x00, x) 2216 | mstore(0x20, y) 2217 | } 2218 | mstore(0x40, mulmod(addmod(mload(C_U_LOC), 0x1, p), mload(C_V23_LOC), p)) 2219 | // accumulator_2 = u.[table3] 2220 | success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40)) 2221 | // accumulator = accumulator + accumulator_2 2222 | success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40)) 2223 | 2224 | // VALIDATE TABLE4 2225 | { 2226 | let x := mload(TABLE4_X_LOC) 2227 | let y := mload(TABLE4_Y_LOC) 2228 | let xx := mulmod(x, x, q) 2229 | // validate on curve 2230 | success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q))) 2231 | mstore(0x00, x) 2232 | mstore(0x20, y) 2233 | } 2234 | mstore(0x40, mulmod(addmod(mload(C_U_LOC), 0x1, p), mload(C_V24_LOC), p)) 2235 | // accumulator_2 = u.[table4] 2236 | success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40)) 2237 | // accumulator = accumulator + accumulator_2 2238 | success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40)) 2239 | 2240 | // VALIDATE TABLE_TYPE 2241 | { 2242 | let x := mload(TABLE_TYPE_X_LOC) 2243 | let y := mload(TABLE_TYPE_Y_LOC) 2244 | let xx := mulmod(x, x, q) 2245 | // validate on curve 2246 | success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q))) 2247 | mstore(0x00, x) 2248 | mstore(0x20, y) 2249 | } 2250 | mstore(0x40, mload(C_V25_LOC)) 2251 | // accumulator_2 = v25.[TableType] 2252 | success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40)) 2253 | // accumulator = accumulator + accumulator_2 2254 | success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40)) 2255 | 2256 | // VALIDATE ID1 2257 | { 2258 | let x := mload(ID1_X_LOC) 2259 | let y := mload(ID1_Y_LOC) 2260 | let xx := mulmod(x, x, q) 2261 | // validate on curve 2262 | success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q))) 2263 | mstore(0x00, x) 2264 | mstore(0x20, y) 2265 | } 2266 | mstore(0x40, mload(C_V26_LOC)) 2267 | // accumulator_2 = v26.[ID1] 2268 | success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40)) 2269 | // accumulator = accumulator + accumulator_2 2270 | success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40)) 2271 | 2272 | // VALIDATE ID2 2273 | { 2274 | let x := mload(ID2_X_LOC) 2275 | let y := mload(ID2_Y_LOC) 2276 | let xx := mulmod(x, x, q) 2277 | // validate on curve 2278 | success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q))) 2279 | mstore(0x00, x) 2280 | mstore(0x20, y) 2281 | } 2282 | mstore(0x40, mload(C_V27_LOC)) 2283 | // accumulator_2 = v27.[ID2] 2284 | success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40)) 2285 | // accumulator = accumulator + accumulator_2 2286 | success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40)) 2287 | 2288 | // VALIDATE ID3 2289 | { 2290 | let x := mload(ID3_X_LOC) 2291 | let y := mload(ID3_Y_LOC) 2292 | let xx := mulmod(x, x, q) 2293 | // validate on curve 2294 | success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q))) 2295 | mstore(0x00, x) 2296 | mstore(0x20, y) 2297 | } 2298 | mstore(0x40, mload(C_V28_LOC)) 2299 | // accumulator_2 = v28.[ID3] 2300 | success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40)) 2301 | // accumulator = accumulator + accumulator_2 2302 | success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40)) 2303 | 2304 | // VALIDATE ID4 2305 | { 2306 | let x := mload(ID4_X_LOC) 2307 | let y := mload(ID4_Y_LOC) 2308 | let xx := mulmod(x, x, q) 2309 | // validate on curve 2310 | success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q))) 2311 | mstore(0x00, x) 2312 | mstore(0x20, y) 2313 | } 2314 | mstore(0x40, mload(C_V29_LOC)) 2315 | // accumulator_2 = v29.[ID4] 2316 | success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40)) 2317 | // accumulator = accumulator + accumulator_2 2318 | success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40)) 2319 | 2320 | /** 2321 | * COMPUTE BATCH EVALUATION SCALAR MULTIPLIER 2322 | */ 2323 | { 2324 | /** 2325 | * batch_evaluation = v0 * (w_1_omega * u + w_1_eval) 2326 | * batch_evaluation += v1 * (w_2_omega * u + w_2_eval) 2327 | * batch_evaluation += v2 * (w_3_omega * u + w_3_eval) 2328 | * batch_evaluation += v3 * (w_4_omega * u + w_4_eval) 2329 | * batch_evaluation += v4 * (s_omega_eval * u + s_eval) 2330 | * batch_evaluation += v5 * (z_omega_eval * u + z_eval) 2331 | * batch_evaluation += v6 * (z_lookup_omega_eval * u + z_lookup_eval) 2332 | */ 2333 | let batch_evaluation := 2334 | mulmod( 2335 | mload(C_V0_LOC), 2336 | addmod(mulmod(mload(W1_OMEGA_EVAL_LOC), mload(C_U_LOC), p), mload(W1_EVAL_LOC), p), 2337 | p 2338 | ) 2339 | batch_evaluation := 2340 | addmod( 2341 | batch_evaluation, 2342 | mulmod( 2343 | mload(C_V1_LOC), 2344 | addmod(mulmod(mload(W2_OMEGA_EVAL_LOC), mload(C_U_LOC), p), mload(W2_EVAL_LOC), p), 2345 | p 2346 | ), 2347 | p 2348 | ) 2349 | batch_evaluation := 2350 | addmod( 2351 | batch_evaluation, 2352 | mulmod( 2353 | mload(C_V2_LOC), 2354 | addmod(mulmod(mload(W3_OMEGA_EVAL_LOC), mload(C_U_LOC), p), mload(W3_EVAL_LOC), p), 2355 | p 2356 | ), 2357 | p 2358 | ) 2359 | batch_evaluation := 2360 | addmod( 2361 | batch_evaluation, 2362 | mulmod( 2363 | mload(C_V3_LOC), 2364 | addmod(mulmod(mload(W4_OMEGA_EVAL_LOC), mload(C_U_LOC), p), mload(W4_EVAL_LOC), p), 2365 | p 2366 | ), 2367 | p 2368 | ) 2369 | batch_evaluation := 2370 | addmod( 2371 | batch_evaluation, 2372 | mulmod( 2373 | mload(C_V4_LOC), 2374 | addmod(mulmod(mload(S_OMEGA_EVAL_LOC), mload(C_U_LOC), p), mload(S_EVAL_LOC), p), 2375 | p 2376 | ), 2377 | p 2378 | ) 2379 | batch_evaluation := 2380 | addmod( 2381 | batch_evaluation, 2382 | mulmod( 2383 | mload(C_V5_LOC), 2384 | addmod(mulmod(mload(Z_OMEGA_EVAL_LOC), mload(C_U_LOC), p), mload(Z_EVAL_LOC), p), 2385 | p 2386 | ), 2387 | p 2388 | ) 2389 | batch_evaluation := 2390 | addmod( 2391 | batch_evaluation, 2392 | mulmod( 2393 | mload(C_V6_LOC), 2394 | addmod(mulmod(mload(Z_LOOKUP_OMEGA_EVAL_LOC), mload(C_U_LOC), p), mload(Z_LOOKUP_EVAL_LOC), p), 2395 | p 2396 | ), 2397 | p 2398 | ) 2399 | 2400 | /** 2401 | * batch_evaluation += v7 * Q1_EVAL 2402 | * batch_evaluation += v8 * Q2_EVAL 2403 | * batch_evaluation += v9 * Q3_EVAL 2404 | * batch_evaluation += v10 * Q4_EVAL 2405 | * batch_evaluation += v11 * QM_EVAL 2406 | * batch_evaluation += v12 * QC_EVAL 2407 | * batch_evaluation += v13 * QARITH_EVAL 2408 | * batch_evaluation += v14 * QSORT_EVAL_LOC 2409 | * batch_evaluation += v15 * QELLIPTIC_EVAL_LOC 2410 | * batch_evaluation += v16 * QAUX_EVAL_LOC 2411 | * batch_evaluation += v17 * SIGMA1_EVAL_LOC 2412 | * batch_evaluation += v18 * SIGMA2_EVAL_LOC 2413 | * batch_evaluation += v19 * SIGMA3_EVAL_LOC 2414 | * batch_evaluation += v20 * SIGMA4_EVAL_LOC 2415 | */ 2416 | batch_evaluation := addmod(batch_evaluation, mulmod(mload(C_V7_LOC), mload(Q1_EVAL_LOC), p), p) 2417 | batch_evaluation := addmod(batch_evaluation, mulmod(mload(C_V8_LOC), mload(Q2_EVAL_LOC), p), p) 2418 | batch_evaluation := addmod(batch_evaluation, mulmod(mload(C_V9_LOC), mload(Q3_EVAL_LOC), p), p) 2419 | batch_evaluation := addmod(batch_evaluation, mulmod(mload(C_V10_LOC), mload(Q4_EVAL_LOC), p), p) 2420 | batch_evaluation := addmod(batch_evaluation, mulmod(mload(C_V11_LOC), mload(QM_EVAL_LOC), p), p) 2421 | batch_evaluation := addmod(batch_evaluation, mulmod(mload(C_V12_LOC), mload(QC_EVAL_LOC), p), p) 2422 | batch_evaluation := addmod(batch_evaluation, mulmod(mload(C_V13_LOC), mload(QARITH_EVAL_LOC), p), p) 2423 | batch_evaluation := addmod(batch_evaluation, mulmod(mload(C_V14_LOC), mload(QSORT_EVAL_LOC), p), p) 2424 | batch_evaluation := addmod(batch_evaluation, mulmod(mload(C_V15_LOC), mload(QELLIPTIC_EVAL_LOC), p), p) 2425 | batch_evaluation := addmod(batch_evaluation, mulmod(mload(C_V16_LOC), mload(QAUX_EVAL_LOC), p), p) 2426 | batch_evaluation := addmod(batch_evaluation, mulmod(mload(C_V17_LOC), mload(SIGMA1_EVAL_LOC), p), p) 2427 | batch_evaluation := addmod(batch_evaluation, mulmod(mload(C_V18_LOC), mload(SIGMA2_EVAL_LOC), p), p) 2428 | batch_evaluation := addmod(batch_evaluation, mulmod(mload(C_V19_LOC), mload(SIGMA3_EVAL_LOC), p), p) 2429 | batch_evaluation := addmod(batch_evaluation, mulmod(mload(C_V20_LOC), mload(SIGMA4_EVAL_LOC), p), p) 2430 | 2431 | /** 2432 | * batch_evaluation += v21 * (table1(zw) * u + table1(z)) 2433 | * batch_evaluation += v22 * (table2(zw) * u + table2(z)) 2434 | * batch_evaluation += v23 * (table3(zw) * u + table3(z)) 2435 | * batch_evaluation += v24 * (table4(zw) * u + table4(z)) 2436 | * batch_evaluation += v25 * table_type_eval 2437 | * batch_evaluation += v26 * id1_eval 2438 | * batch_evaluation += v27 * id2_eval 2439 | * batch_evaluation += v28 * id3_eval 2440 | * batch_evaluation += v29 * id4_eval 2441 | * batch_evaluation += quotient_eval 2442 | */ 2443 | batch_evaluation := 2444 | addmod( 2445 | batch_evaluation, 2446 | mulmod( 2447 | mload(C_V21_LOC), 2448 | addmod(mulmod(mload(TABLE1_OMEGA_EVAL_LOC), mload(C_U_LOC), p), mload(TABLE1_EVAL_LOC), p), 2449 | p 2450 | ), 2451 | p 2452 | ) 2453 | batch_evaluation := 2454 | addmod( 2455 | batch_evaluation, 2456 | mulmod( 2457 | mload(C_V22_LOC), 2458 | addmod(mulmod(mload(TABLE2_OMEGA_EVAL_LOC), mload(C_U_LOC), p), mload(TABLE2_EVAL_LOC), p), 2459 | p 2460 | ), 2461 | p 2462 | ) 2463 | batch_evaluation := 2464 | addmod( 2465 | batch_evaluation, 2466 | mulmod( 2467 | mload(C_V23_LOC), 2468 | addmod(mulmod(mload(TABLE3_OMEGA_EVAL_LOC), mload(C_U_LOC), p), mload(TABLE3_EVAL_LOC), p), 2469 | p 2470 | ), 2471 | p 2472 | ) 2473 | batch_evaluation := 2474 | addmod( 2475 | batch_evaluation, 2476 | mulmod( 2477 | mload(C_V24_LOC), 2478 | addmod(mulmod(mload(TABLE4_OMEGA_EVAL_LOC), mload(C_U_LOC), p), mload(TABLE4_EVAL_LOC), p), 2479 | p 2480 | ), 2481 | p 2482 | ) 2483 | batch_evaluation := addmod(batch_evaluation, mulmod(mload(C_V25_LOC), mload(TABLE_TYPE_EVAL_LOC), p), p) 2484 | batch_evaluation := addmod(batch_evaluation, mulmod(mload(C_V26_LOC), mload(ID1_EVAL_LOC), p), p) 2485 | batch_evaluation := addmod(batch_evaluation, mulmod(mload(C_V27_LOC), mload(ID2_EVAL_LOC), p), p) 2486 | batch_evaluation := addmod(batch_evaluation, mulmod(mload(C_V28_LOC), mload(ID3_EVAL_LOC), p), p) 2487 | batch_evaluation := addmod(batch_evaluation, mulmod(mload(C_V29_LOC), mload(ID4_EVAL_LOC), p), p) 2488 | batch_evaluation := addmod(batch_evaluation, mload(QUOTIENT_EVAL_LOC), p) 2489 | 2490 | mstore(0x00, 0x01) // [1].x 2491 | mstore(0x20, 0x02) // [1].y 2492 | mstore(0x40, sub(p, batch_evaluation)) 2493 | // accumulator_2 = -[1].(batch_evaluation) 2494 | success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40)) 2495 | // accumulator = accumulator + accumulator_2 2496 | success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40)) 2497 | 2498 | mstore(OPENING_COMMITMENT_SUCCESS_FLAG, success) 2499 | } 2500 | 2501 | /** 2502 | * PERFORM PAIRING PREAMBLE 2503 | */ 2504 | { 2505 | let u := mload(C_U_LOC) 2506 | let zeta := mload(C_ZETA_LOC) 2507 | // VALIDATE PI_Z 2508 | { 2509 | let x := mload(PI_Z_X_LOC) 2510 | let y := mload(PI_Z_Y_LOC) 2511 | let xx := mulmod(x, x, q) 2512 | // validate on curve 2513 | success := eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)) 2514 | mstore(0x00, x) 2515 | mstore(0x20, y) 2516 | } 2517 | // compute zeta.[PI_Z] and add into accumulator 2518 | mstore(0x40, zeta) 2519 | success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40)) 2520 | // accumulator = accumulator + accumulator_2 2521 | success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40)) 2522 | 2523 | // VALIDATE PI_Z_OMEGA 2524 | { 2525 | let x := mload(PI_Z_OMEGA_X_LOC) 2526 | let y := mload(PI_Z_OMEGA_Y_LOC) 2527 | let xx := mulmod(x, x, q) 2528 | // validate on curve 2529 | success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q))) 2530 | mstore(0x00, x) 2531 | mstore(0x20, y) 2532 | } 2533 | mstore(0x40, mulmod(mulmod(u, zeta, p), mload(OMEGA_LOC), p)) 2534 | // accumulator_2 = u.zeta.omega.[PI_Z_OMEGA] 2535 | success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40)) 2536 | // PAIRING_RHS = accumulator + accumulator_2 2537 | success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, PAIRING_RHS_X_LOC, 0x40)) 2538 | 2539 | mstore(0x00, mload(PI_Z_X_LOC)) 2540 | mstore(0x20, mload(PI_Z_Y_LOC)) 2541 | mstore(0x40, mload(PI_Z_OMEGA_X_LOC)) 2542 | mstore(0x60, mload(PI_Z_OMEGA_Y_LOC)) 2543 | mstore(0x80, u) 2544 | success := and(success, staticcall(gas(), 7, 0x40, 0x60, 0x40, 0x40)) 2545 | // PAIRING_LHS = [PI_Z] + [PI_Z_OMEGA] * u 2546 | success := and(success, staticcall(gas(), 6, 0x00, 0x80, PAIRING_LHS_X_LOC, 0x40)) 2547 | // negate lhs y-coordinate 2548 | mstore(PAIRING_LHS_Y_LOC, sub(q, mload(PAIRING_LHS_Y_LOC))) 2549 | 2550 | if mload(CONTAINS_RECURSIVE_PROOF_LOC) { 2551 | // VALIDATE RECURSIVE P1 2552 | { 2553 | let x := mload(RECURSIVE_P1_X_LOC) 2554 | let y := mload(RECURSIVE_P1_Y_LOC) 2555 | let xx := mulmod(x, x, q) 2556 | // validate on curve 2557 | success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q))) 2558 | mstore(0x00, x) 2559 | mstore(0x20, y) 2560 | } 2561 | 2562 | // compute u.u.[recursive_p1] and write into 0x60 2563 | mstore(0x40, mulmod(u, u, p)) 2564 | success := and(success, staticcall(gas(), 7, 0x00, 0x60, 0x60, 0x40)) 2565 | // VALIDATE RECURSIVE P2 2566 | { 2567 | let x := mload(RECURSIVE_P2_X_LOC) 2568 | let y := mload(RECURSIVE_P2_Y_LOC) 2569 | let xx := mulmod(x, x, q) 2570 | // validate on curve 2571 | success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q))) 2572 | mstore(0x00, x) 2573 | mstore(0x20, y) 2574 | } 2575 | // compute u.u.[recursive_p2] and write into 0x00 2576 | // 0x40 still contains u*u 2577 | success := and(success, staticcall(gas(), 7, 0x00, 0x60, 0x00, 0x40)) 2578 | 2579 | // compute u.u.[recursiveP1] + rhs and write into rhs 2580 | mstore(0xa0, mload(PAIRING_RHS_X_LOC)) 2581 | mstore(0xc0, mload(PAIRING_RHS_Y_LOC)) 2582 | success := and(success, staticcall(gas(), 6, 0x60, 0x80, PAIRING_RHS_X_LOC, 0x40)) 2583 | 2584 | // compute u.u.[recursiveP2] + lhs and write into lhs 2585 | mstore(0x40, mload(PAIRING_LHS_X_LOC)) 2586 | mstore(0x60, mload(PAIRING_LHS_Y_LOC)) 2587 | success := and(success, staticcall(gas(), 6, 0x00, 0x80, PAIRING_LHS_X_LOC, 0x40)) 2588 | } 2589 | 2590 | if iszero(success) { 2591 | mstore(0x0, EC_SCALAR_MUL_FAILURE_SELECTOR) 2592 | revert(0x00, 0x04) 2593 | } 2594 | mstore(PAIRING_PREAMBLE_SUCCESS_FLAG, success) 2595 | } 2596 | 2597 | /** 2598 | * PERFORM PAIRING 2599 | */ 2600 | { 2601 | // rhs paired with [1]_2 2602 | // lhs paired with [x]_2 2603 | 2604 | mstore(0x00, mload(PAIRING_RHS_X_LOC)) 2605 | mstore(0x20, mload(PAIRING_RHS_Y_LOC)) 2606 | mstore(0x40, 0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2) // this is [1]_2 2607 | mstore(0x60, 0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed) 2608 | mstore(0x80, 0x090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b) 2609 | mstore(0xa0, 0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa) 2610 | 2611 | mstore(0xc0, mload(PAIRING_LHS_X_LOC)) 2612 | mstore(0xe0, mload(PAIRING_LHS_Y_LOC)) 2613 | mstore(0x100, mload(G2X_X0_LOC)) 2614 | mstore(0x120, mload(G2X_X1_LOC)) 2615 | mstore(0x140, mload(G2X_Y0_LOC)) 2616 | mstore(0x160, mload(G2X_Y1_LOC)) 2617 | 2618 | success := staticcall(gas(), 8, 0x00, 0x180, 0x00, 0x20) 2619 | mstore(PAIRING_SUCCESS_FLAG, success) 2620 | mstore(RESULT_FLAG, mload(0x00)) 2621 | } 2622 | if iszero( 2623 | and( 2624 | and(and(mload(PAIRING_SUCCESS_FLAG), mload(RESULT_FLAG)), mload(PAIRING_PREAMBLE_SUCCESS_FLAG)), 2625 | mload(OPENING_COMMITMENT_SUCCESS_FLAG) 2626 | ) 2627 | ) { 2628 | mstore(0x0, PROOF_FAILURE_SELECTOR) 2629 | revert(0x00, 0x04) 2630 | } 2631 | { 2632 | mstore(0x00, 0x01) 2633 | return(0x00, 0x20) // Proof succeeded! 2634 | } 2635 | } 2636 | } 2637 | } 2638 | 2639 | contract UltraVerifier is BaseUltraVerifier { 2640 | function getVerificationKeyHash() public pure override(BaseUltraVerifier) returns (bytes32) { 2641 | return UltraVerificationKey.verificationKeyHash(); 2642 | } 2643 | 2644 | function loadVerificationKey(uint256 vk, uint256 _omegaInverseLoc) internal pure virtual override(BaseUltraVerifier) { 2645 | UltraVerificationKey.loadVerificationKey(vk, _omegaInverseLoc); 2646 | } 2647 | } 2648 | -------------------------------------------------------------------------------- /circuit/src/main.nr: -------------------------------------------------------------------------------- 1 | use dep::std::ecdsa_secp256k1::verify_signature; 2 | 3 | fn main( 4 | pub_key_x: [u8; 32], 5 | pub_key_y: [u8; 32], 6 | signature: [u8; 64], 7 | hashed_message: pub [u8; 32], 8 | ) { 9 | let valid_signature = verify_signature(pub_key_x, pub_key_y, signature, hashed_message); 10 | assert(valid_signature); 11 | } -------------------------------------------------------------------------------- /circuit/target/circuit.json: -------------------------------------------------------------------------------- 1 | {"noir_version":"0.19.4+4d133c50a50f21ca23861a9d1987207bd8783d36","hash":14454786671665241064,"backend":"acvm-backend-barretenberg","abi":{"parameters":[{"name":"pub_key_x","type":{"kind":"array","length":32,"type":{"kind":"integer","sign":"unsigned","width":8}},"visibility":"private"},{"name":"pub_key_y","type":{"kind":"array","length":32,"type":{"kind":"integer","sign":"unsigned","width":8}},"visibility":"private"},{"name":"signature","type":{"kind":"array","length":64,"type":{"kind":"integer","sign":"unsigned","width":8}},"visibility":"private"},{"name":"hashed_message","type":{"kind":"array","length":32,"type":{"kind":"integer","sign":"unsigned","width":8}},"visibility":"public"}],"param_witnesses":{"hashed_message":[{"start":129,"end":161}],"pub_key_x":[{"start":1,"end":33}],"pub_key_y":[{"start":33,"end":65}],"signature":[{"start":65,"end":129}]},"return_type":null,"return_witnesses":[]},"bytecode":"H4sIAAAAAAAA/6WWZW/bZxxF/8moY2ZKO8rYju3EHjXtKGNmSrw6y5iZMigzc5v0k7ZHfq7knL7sI1k/nbywIkv33LtYVdWJqv+GTn2Gy102wMPis8Rni88Rnys+T7xMfL74AvGF4ovEF4svEV8qvkx8ufgK8ZXiq8RXi68RXyu+Tny9+AbxjeKbxDeLbxHfKh4RLxevEN8mvl18h/hO8V3iUfHd4nvE94rvE98vfkD8oLgmrovHxA1xU9wSj4snxG1xR/yQ+GHxI+JHxY+JV4onxavEq8WPi58QPyl+Svy0eEr8jPhZ8XPi58UviF8UvyR+WfyK+FXxa+LXxW+I3xS/JX5b/I74XfF74vfFH4g/FH8knhbPiLvij8VrxD3xrPgT8Zz4U/Fn4s/FX4i/FH8l/lr8jfhb8Xfi78U/iH8U/yT+WfyL+Ffxb+LfxX+I/xT/Jf5bPC/+R/yv+D/x/+K14nXi9eIN4o3iTeLN4i3ireJt4u3iHeKd4l3i3eI94r3ifeL94gPig+JD4sPiI+Kj4mPi4+KFAWZ7jVT9N1T+Plxudl/2XnZe9l12XT7Zcdlv2W3Za9lp2WfZZdlj2WHZX9ld2VvZWdlX2VXZU9lR2U/ZTdlL2UnZR9lFIwOXt7zwinKzf7J7sneyc7JvRsvNnsmOyX7JbsleyU7JPskuyR7JDsn+yO7I3sjOyL7IrsieyI7IfshuyF7ITsg+mBy4vFWFV5ebHZD+T++n79PzU+Wm19Pn6fH0d3o7fZ2eTj+nl9PH6eH0b3o3fZueTb+mV9On6dH0Z3ozfZmenC53ptxuuenB9F96L32XnpsrN72WPkuPpb/SW+mr9FT6Kb2UPkoPpX/SO+mb9Ez6Jb2SPkmPpD/SG+mL9MR8tTQP6Yf0QvogPRD/x/vx/YZy4/d4PT6Px+PveDu+jqfj53g5Po6H4994N76NZ+PXeDU+jUfjz3gzvlwod7Fa+obKnSy3dmavvjjwXY3aeLPZmxjr1Rv1mdpYp9tu1Zqt7ni73q632q01Y+1Go9dutic63c5ErVNvNnr12VanMVu+bH7gf8TjOBx/4268HWfjajyNo/EzbsbLOBkf42I8jIPxL+7FuzgX3+JaPItj8Stuxas4daTqOxR/4k68iTPxJa4crfqOxI+4ES/iRHyIC/EgDsR/uA/v4Tx8h+vwHI7Db7gNr+E0fLay/Ja4C2/hLHyFq/AUjpqq+m7CSzgJH+EiPISD8A/uwTs4B9/gGjyDY/ALbsErOAWf4BI8gkOmq7478AbOwBe4Ak/giLmq7wa8gBPwAS7AAziA/JN9ck/myTtZJ+dknHyTbXJNpskzWSbHZHi+WppdcktmyStZJadklHySTXJJJskjWSSHZJD8kT1yR+bIG1kjZ2SMfJEtckWmyBNZIkdkaKE6/Z0Etse+D4ASAAA="} -------------------------------------------------------------------------------- /circuit/target/debug_circuit.json: -------------------------------------------------------------------------------- 1 | {"debug_symbols":[{"locations":{"160":[{"span":{"start":190,"end":255},"file":1}],"161":[{"span":{"start":268,"end":283},"file":1}]}}],"file_map":{"1":{"source":"use dep::std::ecdsa_secp256k1::verify_signature;\n\nfn main(\n pub_key_x: [u8; 32],\n pub_key_y: [u8; 32],\n signature: [u8; 64],\n hashed_message: pub [u8; 32],\n) {\n let valid_signature = verify_signature(pub_key_x, pub_key_y, signature, hashed_message);\n assert(valid_signature);\n}","path":"/home/turupawn/Projects/NEGRO/test01/circuit/src/main.nr"}},"warnings":[]} -------------------------------------------------------------------------------- /contracts/CommentVerifier.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.23; 3 | 4 | interface IUltraVerifier { 5 | function verify(bytes calldata _proof, bytes32[] calldata _publicInputs) external view returns (bool); 6 | } 7 | 8 | abstract contract Utils { 9 | // ADDRESS TO STRING 10 | // From 0age on Stackoverflow 11 | //https://ethereum.stackexchange.com/questions/63908/address-checksum-solidity-implementation 12 | function toChecksumString( address account) internal pure returns (string memory asciiString) { 13 | // convert the account argument from address to bytes. 14 | bytes20 data = bytes20(account); 15 | 16 | // create an in-memory fixed-size bytes array. 17 | bytes memory asciiBytes = new bytes(40); 18 | 19 | // declare variable types. 20 | uint8 b; 21 | uint8 leftNibble; 22 | uint8 rightNibble; 23 | bool leftCaps; 24 | bool rightCaps; 25 | uint8 asciiOffset; 26 | 27 | // get the capitalized characters in the actual checksum. 28 | bool[40] memory caps = _toChecksumCapsFlags(account); 29 | 30 | // iterate over bytes, processing left and right nibble in each iteration. 31 | for (uint256 i = 0; i < data.length; i++) { 32 | // locate the byte and extract each nibble. 33 | b = uint8(uint160(data) / (2**(8*(19 - i)))); 34 | leftNibble = b / 16; 35 | rightNibble = b - 16 * leftNibble; 36 | 37 | // locate and extract each capitalization status. 38 | leftCaps = caps[2*i]; 39 | rightCaps = caps[2*i + 1]; 40 | 41 | // get the offset from nibble value to ascii character for left nibble. 42 | asciiOffset = _getAsciiOffset(leftNibble, leftCaps); 43 | 44 | // add the converted character to the byte array. 45 | asciiBytes[2 * i] = bytes1(leftNibble + asciiOffset); 46 | 47 | // get the offset from nibble value to ascii character for right nibble. 48 | asciiOffset = _getAsciiOffset(rightNibble, rightCaps); 49 | 50 | // add the converted character to the byte array. 51 | asciiBytes[2 * i + 1] = bytes1(rightNibble + asciiOffset); 52 | } 53 | 54 | return string(asciiBytes); 55 | } 56 | 57 | function _toChecksumCapsFlags(address account) internal pure returns (bool[40] memory characterCapitalized) { 58 | // convert the address to bytes. 59 | bytes20 a = bytes20(account); 60 | 61 | // hash the address (used to calculate checksum). 62 | bytes32 b = keccak256(abi.encodePacked(_toAsciiString(a))); 63 | 64 | // declare variable types. 65 | uint8 leftNibbleAddress; 66 | uint8 rightNibbleAddress; 67 | uint8 leftNibbleHash; 68 | uint8 rightNibbleHash; 69 | 70 | // iterate over bytes, processing left and right nibble in each iteration. 71 | for (uint256 i; i < a.length; i++) { 72 | // locate the byte and extract each nibble for the address and the hash. 73 | rightNibbleAddress = uint8(a[i]) % 16; 74 | leftNibbleAddress = (uint8(a[i]) - rightNibbleAddress) / 16; 75 | rightNibbleHash = uint8(b[i]) % 16; 76 | leftNibbleHash = (uint8(b[i]) - rightNibbleHash) / 16; 77 | 78 | characterCapitalized[2 * i] = ( 79 | leftNibbleAddress > 9 && 80 | leftNibbleHash > 7 81 | ); 82 | characterCapitalized[2 * i + 1] = ( 83 | rightNibbleAddress > 9 && 84 | rightNibbleHash > 7 85 | ); 86 | } 87 | } 88 | 89 | function _getAsciiOffset(uint8 nibble, bool caps) internal pure returns (uint8 offset) { 90 | // to convert to ascii characters, add 48 to 0-9, 55 to A-F, & 87 to a-f. 91 | if (nibble < 10) { 92 | offset = 48; 93 | } else if (caps) { 94 | offset = 55; 95 | } else { 96 | offset = 87; 97 | } 98 | } 99 | 100 | 101 | // based on https://ethereum.stackexchange.com/a/56499/48410 102 | function _toAsciiString(bytes20 data) internal pure returns (string memory asciiString) { 103 | // create an in-memory fixed-size bytes array. 104 | bytes memory asciiBytes = new bytes(40); 105 | 106 | // declare variable types. 107 | uint8 b; 108 | uint8 leftNibble; 109 | uint8 rightNibble; 110 | 111 | // iterate over bytes, processing left and right nibble in each iteration. 112 | for (uint256 i = 0; i < data.length; i++) { 113 | // locate the byte and extract each nibble. 114 | b = uint8(uint160(data) / (2 ** (8 * (19 - i)))); 115 | leftNibble = b / 16; 116 | rightNibble = b - 16 * leftNibble; 117 | 118 | // to convert to ascii characters, add 48 to 0-9 and 87 to a-f. 119 | asciiBytes[2 * i] = bytes1(leftNibble + (leftNibble < 10 ? 48 : 87)); 120 | asciiBytes[2 * i + 1] = bytes1(rightNibble + (rightNibble < 10 ? 48 : 87)); 121 | } 122 | 123 | return string(asciiBytes); 124 | } 125 | 126 | // UINT TO STRING 127 | // From OpenZeppelin 128 | // https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/Strings.sol 129 | function log10(uint256 value) internal pure returns (uint256) { 130 | uint256 result = 0; 131 | unchecked { 132 | if (value >= 10 ** 64) { 133 | value /= 10 ** 64; 134 | result += 64; 135 | } 136 | if (value >= 10 ** 32) { 137 | value /= 10 ** 32; 138 | result += 32; 139 | } 140 | if (value >= 10 ** 16) { 141 | value /= 10 ** 16; 142 | result += 16; 143 | } 144 | if (value >= 10 ** 8) { 145 | value /= 10 ** 8; 146 | result += 8; 147 | } 148 | if (value >= 10 ** 4) { 149 | value /= 10 ** 4; 150 | result += 4; 151 | } 152 | if (value >= 10 ** 2) { 153 | value /= 10 ** 2; 154 | result += 2; 155 | } 156 | if (value >= 10 ** 1) { 157 | result += 1; 158 | } 159 | } 160 | return result; 161 | } 162 | 163 | bytes16 private constant HEX_DIGITS = "0123456789abcdef"; 164 | 165 | function toString(uint256 value) internal pure returns (string memory) { 166 | unchecked { 167 | uint256 length = log10(value) + 1; 168 | string memory buffer = new string(length); 169 | uint256 ptr; 170 | /// @solidity memory-safe-assembly 171 | assembly { 172 | ptr := add(buffer, add(32, length)) 173 | } 174 | while (true) { 175 | ptr--; 176 | /// @solidity memory-safe-assembly 177 | assembly { 178 | mstore8(ptr, byte(mod(value, 10), HEX_DIGITS)) 179 | } 180 | value /= 10; 181 | if (value == 0) break; 182 | } 183 | return buffer; 184 | } 185 | } 186 | 187 | // CONCATENATE HEX ARRAY 188 | // From ChatGPT 189 | function concatenateHexArray(bytes32[] memory hexArray) internal pure returns (bytes32) { 190 | bytes32 result; 191 | for (uint256 i = 0; i < hexArray.length; i++) { 192 | result = result << 8 | hexArray[i]; 193 | } 194 | return result; 195 | } 196 | } 197 | 198 | contract CommentVerifier is Utils { 199 | uint public commentAmount; 200 | mapping(uint commentId => string title) public titles; 201 | mapping(uint commentId => string text) public texts; 202 | IUltraVerifier ultraVerifier; 203 | 204 | string public myAddress; 205 | string public messageHeader; 206 | string public messagePrefix; 207 | string public messageMiddlefix; 208 | string public messageSuffix; 209 | 210 | constructor(address verifierAddress) { 211 | ultraVerifier = IUltraVerifier(verifierAddress); 212 | 213 | myAddress = toChecksumString(address(this)); 214 | messageHeader = "\x19Ethereum Signed Message:\n"; 215 | messagePrefix = string(abi.encodePacked( 216 | "{\"types\":{\"EIP712Domain\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"version\",\"type\":\"string\"},{\"name\":\"chainId\",\"type\":\"uint256\"},{\"name\":\"verifyingContract\",\"type\":\"address\"}],\"Comment\":[{\"name\":\"title\",\"type\":\"string\"},{\"name\":\"text\",\"type\":\"string\"}]},\"primaryType\":\"Comment\",\"domain\":{\"name\":\"Anon Message Board\",\"version\":\"1\",\"chainId\":534351,\"verifyingContract\":\"0x", 217 | myAddress, 218 | "\"},\"message\":{\"title\":\"")); 219 | messageMiddlefix = "\",\"text\":\""; 220 | messageSuffix = "\"}}"; 221 | } 222 | 223 | function isValidHash(bytes32 hash, string memory title, string memory text) public view returns(bool) { 224 | string memory jsonMessage = string(abi.encodePacked(messagePrefix, title, messageMiddlefix, text, messageSuffix)); 225 | return hash == keccak256(abi.encodePacked( 226 | messageHeader, 227 | toString(bytes(jsonMessage).length), 228 | jsonMessage)); 229 | } 230 | 231 | function sendProof(bytes calldata _proof, bytes32[] calldata _publicInputs, string memory title, string memory text) public 232 | { 233 | require(isValidHash(concatenateHexArray(_publicInputs), title, text), "Invalid message hash"); 234 | ultraVerifier.verify(_proof, _publicInputs); 235 | titles[commentAmount] = title; 236 | texts[commentAmount] = text; 237 | commentAmount+=1; 238 | } 239 | } 240 | -------------------------------------------------------------------------------- /relayer/.env.example: -------------------------------------------------------------------------------- 1 | RPC_URL="https://sepolia-rpc.scroll.io" 2 | COMMENT_VERIFIER_ADDRESS = "0x123abc" 3 | RELAYER_PRIVATE_KEY ="123abc" 4 | RELAYER_ADDRESS = "0x123abv" -------------------------------------------------------------------------------- /relayer/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .env -------------------------------------------------------------------------------- /relayer/json_abi/CommentVerifier.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "inputs": [ 4 | { 5 | "internalType": "bytes", 6 | "name": "_proof", 7 | "type": "bytes" 8 | }, 9 | { 10 | "internalType": "bytes32[]", 11 | "name": "_publicInputs", 12 | "type": "bytes32[]" 13 | }, 14 | { 15 | "internalType": "string", 16 | "name": "title", 17 | "type": "string" 18 | }, 19 | { 20 | "internalType": "string", 21 | "name": "text", 22 | "type": "string" 23 | } 24 | ], 25 | "name": "sendProof", 26 | "outputs": [], 27 | "stateMutability": "nonpayable", 28 | "type": "function" 29 | }, 30 | { 31 | "inputs": [], 32 | "stateMutability": "nonpayable", 33 | "type": "constructor" 34 | }, 35 | { 36 | "inputs": [], 37 | "name": "commentAmount", 38 | "outputs": [ 39 | { 40 | "internalType": "uint256", 41 | "name": "", 42 | "type": "uint256" 43 | } 44 | ], 45 | "stateMutability": "view", 46 | "type": "function" 47 | }, 48 | { 49 | "inputs": [ 50 | { 51 | "internalType": "bytes32", 52 | "name": "hash", 53 | "type": "bytes32" 54 | }, 55 | { 56 | "internalType": "string", 57 | "name": "title", 58 | "type": "string" 59 | }, 60 | { 61 | "internalType": "string", 62 | "name": "text", 63 | "type": "string" 64 | } 65 | ], 66 | "name": "isValidHash", 67 | "outputs": [ 68 | { 69 | "internalType": "bool", 70 | "name": "", 71 | "type": "bool" 72 | } 73 | ], 74 | "stateMutability": "view", 75 | "type": "function" 76 | }, 77 | { 78 | "inputs": [], 79 | "name": "messageHeader", 80 | "outputs": [ 81 | { 82 | "internalType": "string", 83 | "name": "", 84 | "type": "string" 85 | } 86 | ], 87 | "stateMutability": "view", 88 | "type": "function" 89 | }, 90 | { 91 | "inputs": [], 92 | "name": "messageMiddlefix", 93 | "outputs": [ 94 | { 95 | "internalType": "string", 96 | "name": "", 97 | "type": "string" 98 | } 99 | ], 100 | "stateMutability": "view", 101 | "type": "function" 102 | }, 103 | { 104 | "inputs": [], 105 | "name": "messagePrefix", 106 | "outputs": [ 107 | { 108 | "internalType": "string", 109 | "name": "", 110 | "type": "string" 111 | } 112 | ], 113 | "stateMutability": "view", 114 | "type": "function" 115 | }, 116 | { 117 | "inputs": [], 118 | "name": "messageSuffix", 119 | "outputs": [ 120 | { 121 | "internalType": "string", 122 | "name": "", 123 | "type": "string" 124 | } 125 | ], 126 | "stateMutability": "view", 127 | "type": "function" 128 | }, 129 | { 130 | "inputs": [], 131 | "name": "myAddress", 132 | "outputs": [ 133 | { 134 | "internalType": "string", 135 | "name": "", 136 | "type": "string" 137 | } 138 | ], 139 | "stateMutability": "view", 140 | "type": "function" 141 | }, 142 | { 143 | "inputs": [ 144 | { 145 | "internalType": "uint256", 146 | "name": "commentId", 147 | "type": "uint256" 148 | } 149 | ], 150 | "name": "texts", 151 | "outputs": [ 152 | { 153 | "internalType": "string", 154 | "name": "text", 155 | "type": "string" 156 | } 157 | ], 158 | "stateMutability": "view", 159 | "type": "function" 160 | }, 161 | { 162 | "inputs": [ 163 | { 164 | "internalType": "uint256", 165 | "name": "commentId", 166 | "type": "uint256" 167 | } 168 | ], 169 | "name": "titles", 170 | "outputs": [ 171 | { 172 | "internalType": "string", 173 | "name": "title", 174 | "type": "string" 175 | } 176 | ], 177 | "stateMutability": "view", 178 | "type": "function" 179 | } 180 | ] -------------------------------------------------------------------------------- /relayer/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "relayer", 3 | "version": "1.0.0", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "relayer", 9 | "version": "1.0.0", 10 | "license": "ISC", 11 | "dependencies": { 12 | "cors": "^2.8.5", 13 | "dotenv": "^10.0.0", 14 | "ethers": "^6.9.0", 15 | "node-fetch": "^3.1.0" 16 | } 17 | }, 18 | "node_modules/@adraffy/ens-normalize": { 19 | "version": "1.10.0", 20 | "resolved": "https://registry.npmjs.org/@adraffy/ens-normalize/-/ens-normalize-1.10.0.tgz", 21 | "integrity": "sha512-nA9XHtlAkYfJxY7bce8DcN7eKxWWCWkU+1GR9d+U6MbNpfwQp8TI7vqOsBsMcHoT4mBu2kypKoSKnghEzOOq5Q==" 22 | }, 23 | "node_modules/aes-js": { 24 | "version": "4.0.0-beta.5", 25 | "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-4.0.0-beta.5.tgz", 26 | "integrity": "sha512-G965FqalsNyrPqgEGON7nIx1e/OVENSgiEIzyC63haUMuvNnwIgIjMs52hlTCKhkBny7A2ORNlfY9Zu+jmGk1Q==" 27 | }, 28 | "node_modules/bufferutil": { 29 | "version": "4.0.8", 30 | "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.8.tgz", 31 | "integrity": "sha512-4T53u4PdgsXqKaIctwF8ifXlRTTmEPJ8iEPWFdGZvcf7sbwYo6FKFEX9eNNAnzFZ7EzJAQ3CJeOtCRA4rDp7Pw==", 32 | "hasInstallScript": true, 33 | "optional": true, 34 | "peer": true, 35 | "dependencies": { 36 | "node-gyp-build": "^4.3.0" 37 | }, 38 | "engines": { 39 | "node": ">=6.14.2" 40 | } 41 | }, 42 | "node_modules/cors": { 43 | "version": "2.8.5", 44 | "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", 45 | "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", 46 | "dependencies": { 47 | "object-assign": "^4", 48 | "vary": "^1" 49 | }, 50 | "engines": { 51 | "node": ">= 0.10" 52 | } 53 | }, 54 | "node_modules/data-uri-to-buffer": { 55 | "version": "4.0.1", 56 | "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", 57 | "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", 58 | "engines": { 59 | "node": ">= 12" 60 | } 61 | }, 62 | "node_modules/dotenv": { 63 | "version": "10.0.0", 64 | "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz", 65 | "integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==", 66 | "engines": { 67 | "node": ">=10" 68 | } 69 | }, 70 | "node_modules/ethers": { 71 | "version": "6.9.0", 72 | "resolved": "https://registry.npmjs.org/ethers/-/ethers-6.9.0.tgz", 73 | "integrity": "sha512-pmfNyQzc2mseLe91FnT2vmNaTt8dDzhxZ/xItAV7uGsF4dI4ek2ufMu3rAkgQETL/TIs0GS5A+U05g9QyWnv3Q==", 74 | "funding": [ 75 | { 76 | "type": "individual", 77 | "url": "https://github.com/sponsors/ethers-io/" 78 | }, 79 | { 80 | "type": "individual", 81 | "url": "https://www.buymeacoffee.com/ricmoo" 82 | } 83 | ], 84 | "dependencies": { 85 | "@adraffy/ens-normalize": "1.10.0", 86 | "@noble/curves": "1.2.0", 87 | "@noble/hashes": "1.3.2", 88 | "@types/node": "18.15.13", 89 | "aes-js": "4.0.0-beta.5", 90 | "tslib": "2.4.0", 91 | "ws": "8.5.0" 92 | }, 93 | "engines": { 94 | "node": ">=14.0.0" 95 | } 96 | }, 97 | "node_modules/ethers/node_modules/@noble/curves": { 98 | "version": "1.2.0", 99 | "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.2.0.tgz", 100 | "integrity": "sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==", 101 | "dependencies": { 102 | "@noble/hashes": "1.3.2" 103 | }, 104 | "funding": { 105 | "url": "https://paulmillr.com/funding/" 106 | } 107 | }, 108 | "node_modules/ethers/node_modules/@noble/hashes": { 109 | "version": "1.3.2", 110 | "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.2.tgz", 111 | "integrity": "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==", 112 | "engines": { 113 | "node": ">= 16" 114 | }, 115 | "funding": { 116 | "url": "https://paulmillr.com/funding/" 117 | } 118 | }, 119 | "node_modules/ethers/node_modules/@types/node": { 120 | "version": "18.15.13", 121 | "resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.13.tgz", 122 | "integrity": "sha512-N+0kuo9KgrUQ1Sn/ifDXsvg0TTleP7rIy4zOBGECxAljqvqfqpTfzx0Q1NUedOixRMBfe2Whhb056a42cWs26Q==" 123 | }, 124 | "node_modules/ethers/node_modules/tslib": { 125 | "version": "2.4.0", 126 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", 127 | "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" 128 | }, 129 | "node_modules/ethers/node_modules/ws": { 130 | "version": "8.5.0", 131 | "resolved": "https://registry.npmjs.org/ws/-/ws-8.5.0.tgz", 132 | "integrity": "sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg==", 133 | "engines": { 134 | "node": ">=10.0.0" 135 | }, 136 | "peerDependencies": { 137 | "bufferutil": "^4.0.1", 138 | "utf-8-validate": "^5.0.2" 139 | }, 140 | "peerDependenciesMeta": { 141 | "bufferutil": { 142 | "optional": true 143 | }, 144 | "utf-8-validate": { 145 | "optional": true 146 | } 147 | } 148 | }, 149 | "node_modules/fetch-blob": { 150 | "version": "3.2.0", 151 | "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", 152 | "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", 153 | "funding": [ 154 | { 155 | "type": "github", 156 | "url": "https://github.com/sponsors/jimmywarting" 157 | }, 158 | { 159 | "type": "paypal", 160 | "url": "https://paypal.me/jimmywarting" 161 | } 162 | ], 163 | "dependencies": { 164 | "node-domexception": "^1.0.0", 165 | "web-streams-polyfill": "^3.0.3" 166 | }, 167 | "engines": { 168 | "node": "^12.20 || >= 14.13" 169 | } 170 | }, 171 | "node_modules/formdata-polyfill": { 172 | "version": "4.0.10", 173 | "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", 174 | "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", 175 | "dependencies": { 176 | "fetch-blob": "^3.1.2" 177 | }, 178 | "engines": { 179 | "node": ">=12.20.0" 180 | } 181 | }, 182 | "node_modules/node-domexception": { 183 | "version": "1.0.0", 184 | "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", 185 | "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", 186 | "funding": [ 187 | { 188 | "type": "github", 189 | "url": "https://github.com/sponsors/jimmywarting" 190 | }, 191 | { 192 | "type": "github", 193 | "url": "https://paypal.me/jimmywarting" 194 | } 195 | ], 196 | "engines": { 197 | "node": ">=10.5.0" 198 | } 199 | }, 200 | "node_modules/node-fetch": { 201 | "version": "3.3.2", 202 | "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", 203 | "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", 204 | "dependencies": { 205 | "data-uri-to-buffer": "^4.0.0", 206 | "fetch-blob": "^3.1.4", 207 | "formdata-polyfill": "^4.0.10" 208 | }, 209 | "engines": { 210 | "node": "^12.20.0 || ^14.13.1 || >=16.0.0" 211 | }, 212 | "funding": { 213 | "type": "opencollective", 214 | "url": "https://opencollective.com/node-fetch" 215 | } 216 | }, 217 | "node_modules/node-gyp-build": { 218 | "version": "4.7.1", 219 | "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.7.1.tgz", 220 | "integrity": "sha512-wTSrZ+8lsRRa3I3H8Xr65dLWSgCvY2l4AOnaeKdPA9TB/WYMPaTcrzf3rXvFoVvjKNVnu0CcWSx54qq9GKRUYg==", 221 | "optional": true, 222 | "peer": true, 223 | "bin": { 224 | "node-gyp-build": "bin.js", 225 | "node-gyp-build-optional": "optional.js", 226 | "node-gyp-build-test": "build-test.js" 227 | } 228 | }, 229 | "node_modules/object-assign": { 230 | "version": "4.1.1", 231 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", 232 | "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", 233 | "engines": { 234 | "node": ">=0.10.0" 235 | } 236 | }, 237 | "node_modules/utf-8-validate": { 238 | "version": "5.0.10", 239 | "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.10.tgz", 240 | "integrity": "sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ==", 241 | "hasInstallScript": true, 242 | "optional": true, 243 | "peer": true, 244 | "dependencies": { 245 | "node-gyp-build": "^4.3.0" 246 | }, 247 | "engines": { 248 | "node": ">=6.14.2" 249 | } 250 | }, 251 | "node_modules/vary": { 252 | "version": "1.1.2", 253 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", 254 | "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", 255 | "engines": { 256 | "node": ">= 0.8" 257 | } 258 | }, 259 | "node_modules/web-streams-polyfill": { 260 | "version": "3.2.1", 261 | "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz", 262 | "integrity": "sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==", 263 | "engines": { 264 | "node": ">= 8" 265 | } 266 | } 267 | } 268 | } 269 | -------------------------------------------------------------------------------- /relayer/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "relayer", 3 | "version": "1.0.0", 4 | "description": "huh", 5 | "main": "index.js", 6 | "type": "module", 7 | "scripts": { 8 | "test": "echo \"Error: no test specified\" && exit 1", 9 | "start": "node relayer.js" 10 | }, 11 | "author": "", 12 | "license": "ISC", 13 | "dependencies": { 14 | "cors": "^2.8.5", 15 | "dotenv": "^10.0.0", 16 | "ethers": "^6.9.0", 17 | "node-fetch": "^3.1.0" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /relayer/relayer.js: -------------------------------------------------------------------------------- 1 | import dotenv from "dotenv" 2 | import fs from "fs" 3 | import cors from "cors" 4 | import express from "express" 5 | import { ethers } from 'ethers'; 6 | 7 | const app = express() 8 | dotenv.config(); 9 | app.use(cors()) 10 | 11 | const JSON_CONTRACT_PATH = "./json_abi/CommentVerifier.json" 12 | const PORT = 8080 13 | var contract 14 | var provider 15 | var signer 16 | 17 | const { RPC_URL, COMMENT_VERIFIER_ADDRESS, RELAYER_PRIVATE_KEY, RELAYER_ADDRESS } = process.env; 18 | 19 | const loadContract = async (data) => { 20 | data = JSON.parse(data); 21 | contract = new ethers.Contract(COMMENT_VERIFIER_ADDRESS, data, signer); 22 | } 23 | 24 | async function initAPI() { 25 | provider = new ethers.JsonRpcProvider(RPC_URL); 26 | signer = new ethers.Wallet(RELAYER_PRIVATE_KEY, provider); 27 | 28 | fs.readFile(JSON_CONTRACT_PATH, 'utf8', function (err,data) { 29 | if (err) { 30 | return console.log(err); 31 | } 32 | loadContract(data) 33 | }); 34 | 35 | app.listen(PORT, () => { 36 | console.log(`Listening to port ${PORT}`) 37 | }) 38 | } 39 | 40 | async function relayMessage(proof, hashedMessage, title, text) 41 | { 42 | const transaction = { 43 | from: RELAYER_ADDRESS, 44 | to: COMMENT_VERIFIER_ADDRESS, 45 | value: '0', 46 | gasPrice: "700000000", // 0.7 gwei 47 | nonce: await provider.getTransactionCount(RELAYER_ADDRESS), 48 | chainId: "534351", 49 | data: contract.interface.encodeFunctionData( 50 | "sendProof",[proof, hashedMessage, title, text] 51 | ) 52 | }; 53 | const signedTransaction = await signer.populateTransaction(transaction); 54 | const transactionResponse = await signer.sendTransaction(signedTransaction); 55 | console.log('🎉 The hash of your transaction is:', transactionResponse.hash); 56 | } 57 | 58 | app.get('/relay', (req, res) => { 59 | var proof = req.query["proof"] 60 | var hashedMessage = req.query["hashedMessage"].split(',') 61 | var title = req.query["title"] 62 | var text = req.query["text"] 63 | 64 | relayMessage(proof, hashedMessage, title, text) 65 | 66 | res.setHeader('Content-Type', 'application/json'); 67 | res.send({ 68 | "message": "the proof was relayed" 69 | }) 70 | }) 71 | initAPI() -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Turupawn/PrivacyDAppDemo/548fe899c86cc2a6ec96c7c300a33d8814036752/screenshot.png -------------------------------------------------------------------------------- /webapp/.env.example: -------------------------------------------------------------------------------- 1 | CHAIN_ID = "534351" 2 | COMMENT_VERIFIER_ADDRESS = "0x123abc" -------------------------------------------------------------------------------- /webapp/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .env -------------------------------------------------------------------------------- /webapp/app.js: -------------------------------------------------------------------------------- 1 | import { BarretenbergBackend } from '@noir-lang/backend_barretenberg'; 2 | import { Noir } from '@noir-lang/noir_js'; 3 | import circuit from '../circuit/target/circuit.json'; 4 | 5 | const NETWORK_ID = process.env.CHAIN_ID 6 | 7 | const METADA_API_URL = "http://localhost:8080" 8 | 9 | const COMMENT_VERIFIER_ADDRESS = process.env.COMMENT_VERIFIER_ADDRESS; 10 | 11 | const MY_CONTRACT_ABI_PATH = "../json_abi/CommentVerifier.json" 12 | var my_contract 13 | 14 | var accounts 15 | var web3 16 | 17 | function metamaskReloadCallback() { 18 | window.ethereum.on('accountsChanged', (accounts) => { 19 | document.getElementById("web3_message").textContent="Se cambió el account, refrescando..."; 20 | window.location.reload() 21 | }) 22 | window.ethereum.on('networkChanged', (accounts) => { 23 | document.getElementById("web3_message").textContent="Se el network, refrescando..."; 24 | window.location.reload() 25 | }) 26 | } 27 | 28 | const getWeb3 = async () => { 29 | return new Promise((resolve, reject) => { 30 | if(document.readyState=="complete") 31 | { 32 | if (window.ethereum) { 33 | const web3 = new Web3(window.ethereum) 34 | window.location.reload() 35 | resolve(web3) 36 | } else { 37 | reject("must install MetaMask") 38 | document.getElementById("web3_message").textContent="Error: Please connect to Metamask"; 39 | } 40 | }else 41 | { 42 | window.addEventListener("load", async () => { 43 | if (window.ethereum) { 44 | const web3 = new Web3(window.ethereum) 45 | resolve(web3) 46 | } else { 47 | reject("must install MetaMask") 48 | document.getElementById("web3_message").textContent="Error: Please install Metamask"; 49 | } 50 | }); 51 | } 52 | }); 53 | }; 54 | 55 | const getContract = async (web3, address, abi_path) => { 56 | const response = await fetch(abi_path); 57 | const data = await response.json(); 58 | 59 | const netId = await web3.eth.net.getId(); 60 | var contract = new web3.eth.Contract( 61 | data, 62 | address 63 | ); 64 | return contract 65 | } 66 | 67 | async function loadDapp() { 68 | metamaskReloadCallback() 69 | document.getElementById("web3_message").textContent="Please connect to Metamask" 70 | var awaitWeb3 = async function () { 71 | web3 = await getWeb3() 72 | web3.eth.net.getId((err, netId) => { 73 | if (netId == NETWORK_ID) { 74 | var awaitContract = async function () { 75 | my_contract = await getContract(web3, COMMENT_VERIFIER_ADDRESS, MY_CONTRACT_ABI_PATH) 76 | document.getElementById("web3_message").textContent="You are connected to Metamask" 77 | onContractInitCallback() 78 | web3.eth.getAccounts(function(err, _accounts){ 79 | accounts = _accounts 80 | if (err != null) 81 | { 82 | console.error("An error occurred: "+err) 83 | } else if (accounts.length > 0) 84 | { 85 | onWalletConnectedCallback() 86 | document.getElementById("account_address").style.display = "block" 87 | } else 88 | { 89 | document.getElementById("connect_button").style.display = "block" 90 | } 91 | }); 92 | }; 93 | awaitContract(); 94 | } else { 95 | document.getElementById("web3_message").textContent="Please connect to Scroll Sepolia"; 96 | } 97 | }); 98 | }; 99 | awaitWeb3(); 100 | } 101 | 102 | async function connectWallet() { 103 | await window.ethereum.request({ method: "eth_requestAccounts" }) 104 | accounts = await web3.eth.getAccounts() 105 | onWalletConnectedCallback() 106 | } 107 | window.connectWallet=connectWallet; 108 | 109 | const onContractInitCallback = async () => { 110 | var commentAmount = await my_contract.methods.commentAmount().call() 111 | var contract_state = "commentAmount: " + commentAmount 112 | 113 | var maxMsgPerPage = 5; 114 | var pageIterator = 0; 115 | var commentsElement = document.getElementById("comments"); 116 | 117 | for(var i=parseInt(commentAmount);i>0;i--) 118 | { 119 | var title = await my_contract.methods.titles(i-1).call() 120 | var text = await my_contract.methods.texts(i-1).call() 121 | var paragraph = document.createElement("p"); 122 | var boldElement = document.createElement("b"); 123 | var textElement = document.createElement("span"); 124 | var brElement = document.createElement("br"); 125 | var titleElement = document.createElement("span"); 126 | titleElement.textContent = title; 127 | textElement.textContent = text; 128 | boldElement.appendChild(titleElement); 129 | paragraph.appendChild(boldElement); 130 | paragraph.appendChild(brElement); 131 | paragraph.appendChild(textElement); 132 | commentsElement.appendChild(paragraph); 133 | pageIterator++ 134 | if(pageIterator >= maxMsgPerPage) 135 | { 136 | break 137 | } 138 | } 139 | document.getElementById("contract_state").textContent = contract_state; 140 | } 141 | 142 | const onWalletConnectedCallback = async () => { 143 | } 144 | 145 | document.addEventListener('DOMContentLoaded', async () => { 146 | loadDapp() 147 | }); 148 | 149 | function splitIntoPairs(str) { 150 | return str.match(/.{1,2}/g) || []; 151 | } 152 | 153 | const sendProof = async (title, text) => { 154 | document.getElementById("web3_message").textContent="Please sign the message ✍️"; 155 | var msgParams = JSON.stringify({ 156 | types: { 157 | EIP712Domain: [ 158 | { name: 'name', type: 'string' }, 159 | { name: 'version', type: 'string' }, 160 | { name: 'chainId', type: 'uint256' }, 161 | { name: 'verifyingContract', type: 'address' }, 162 | ], 163 | Comment: [ 164 | { name: 'title', type: 'string' }, 165 | { name: 'text', type: 'string' } 166 | ], 167 | }, 168 | primaryType: 'Comment', 169 | domain: { 170 | name: 'Anon Message Board', 171 | version: '1', 172 | chainId: parseInt(NETWORK_ID), 173 | verifyingContract: COMMENT_VERIFIER_ADDRESS, 174 | }, 175 | message: { 176 | title: title, 177 | text: text, 178 | }, 179 | }); 180 | 181 | var signature = await ethereum.request({ 182 | method: "eth_signTypedData_v4", 183 | params: [accounts[0], msgParams], 184 | }); 185 | 186 | var hashedMessage = ethers.utils.hashMessage(msgParams) 187 | const hashedMessageArray = ethers.utils.arrayify(hashedMessage) 188 | 189 | var publicKey = ethers.utils.recoverPublicKey(hashedMessage, signature) 190 | publicKey = publicKey.substring(4) 191 | 192 | let pub_key_x = publicKey.substring(0, 64); 193 | let pub_key_y = publicKey.substring(64); 194 | 195 | const backend = new BarretenbergBackend(circuit); 196 | const noir = new Noir(circuit, backend); 197 | 198 | var sSignature = Array.from(ethers.utils.arrayify(signature)) 199 | sSignature.pop() 200 | 201 | const input = { 202 | pub_key_x: Array.from(ethers.utils.arrayify("0x"+pub_key_x)), 203 | pub_key_y: Array.from(ethers.utils.arrayify("0x"+pub_key_y)), 204 | signature: sSignature, 205 | hashed_message: Array.from(hashedMessageArray) 206 | }; 207 | 208 | document.getElementById("web3_message").textContent="Generating proof... ⌛"; 209 | var proof = await noir.generateFinalProof(input); 210 | document.getElementById("web3_message").textContent="Generating proof... ✅"; 211 | 212 | proof = "0x" + ethereumjs.Buffer.Buffer.from(proof.proof).toString('hex') 213 | 214 | var tHashedMessage = splitIntoPairs(hashedMessage.substring(2)) 215 | for(var i=0; i { 234 | console.log("ERROR! Transaction reverted: " + revertReason.receipt.transactionHash) 235 | }); 236 | */ 237 | 238 | 239 | } 240 | 241 | const updateMetadata = async (proof, hashedMessage, title, text) => { 242 | fetch(METADA_API_URL + "/relay?proof=" + proof + "&hashedMessage=" + hashedMessage + "&title=" + title + "&text=" + text) 243 | .then(res => res.json()) 244 | .then(out => 245 | console.log(out)) 246 | .catch(); 247 | } 248 | 249 | 250 | window.sendProof=sendProof; -------------------------------------------------------------------------------- /webapp/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

Aztec Noir - Scroll Demo

4 |

Anon Message Board

5 | 6 | 7 |

8 |

9 | 10 |

Send a comment

11 | title: 12 | text: 13 | 14 | 15 |

Comments

16 |
17 | 18 |

19 |

20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 35 | 36 | -------------------------------------------------------------------------------- /webapp/json_abi/CommentVerifier.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "inputs": [ 4 | { 5 | "internalType": "bytes", 6 | "name": "_proof", 7 | "type": "bytes" 8 | }, 9 | { 10 | "internalType": "bytes32[]", 11 | "name": "_publicInputs", 12 | "type": "bytes32[]" 13 | }, 14 | { 15 | "internalType": "string", 16 | "name": "title", 17 | "type": "string" 18 | }, 19 | { 20 | "internalType": "string", 21 | "name": "text", 22 | "type": "string" 23 | } 24 | ], 25 | "name": "sendProof", 26 | "outputs": [], 27 | "stateMutability": "nonpayable", 28 | "type": "function" 29 | }, 30 | { 31 | "inputs": [], 32 | "stateMutability": "nonpayable", 33 | "type": "constructor" 34 | }, 35 | { 36 | "inputs": [], 37 | "name": "commentAmount", 38 | "outputs": [ 39 | { 40 | "internalType": "uint256", 41 | "name": "", 42 | "type": "uint256" 43 | } 44 | ], 45 | "stateMutability": "view", 46 | "type": "function" 47 | }, 48 | { 49 | "inputs": [ 50 | { 51 | "internalType": "bytes32", 52 | "name": "hash", 53 | "type": "bytes32" 54 | }, 55 | { 56 | "internalType": "string", 57 | "name": "title", 58 | "type": "string" 59 | }, 60 | { 61 | "internalType": "string", 62 | "name": "text", 63 | "type": "string" 64 | } 65 | ], 66 | "name": "isValidHash", 67 | "outputs": [ 68 | { 69 | "internalType": "bool", 70 | "name": "", 71 | "type": "bool" 72 | } 73 | ], 74 | "stateMutability": "view", 75 | "type": "function" 76 | }, 77 | { 78 | "inputs": [], 79 | "name": "messageHeader", 80 | "outputs": [ 81 | { 82 | "internalType": "string", 83 | "name": "", 84 | "type": "string" 85 | } 86 | ], 87 | "stateMutability": "view", 88 | "type": "function" 89 | }, 90 | { 91 | "inputs": [], 92 | "name": "messageMiddlefix", 93 | "outputs": [ 94 | { 95 | "internalType": "string", 96 | "name": "", 97 | "type": "string" 98 | } 99 | ], 100 | "stateMutability": "view", 101 | "type": "function" 102 | }, 103 | { 104 | "inputs": [], 105 | "name": "messagePrefix", 106 | "outputs": [ 107 | { 108 | "internalType": "string", 109 | "name": "", 110 | "type": "string" 111 | } 112 | ], 113 | "stateMutability": "view", 114 | "type": "function" 115 | }, 116 | { 117 | "inputs": [], 118 | "name": "messageSuffix", 119 | "outputs": [ 120 | { 121 | "internalType": "string", 122 | "name": "", 123 | "type": "string" 124 | } 125 | ], 126 | "stateMutability": "view", 127 | "type": "function" 128 | }, 129 | { 130 | "inputs": [], 131 | "name": "myAddress", 132 | "outputs": [ 133 | { 134 | "internalType": "string", 135 | "name": "", 136 | "type": "string" 137 | } 138 | ], 139 | "stateMutability": "view", 140 | "type": "function" 141 | }, 142 | { 143 | "inputs": [ 144 | { 145 | "internalType": "uint256", 146 | "name": "commentId", 147 | "type": "uint256" 148 | } 149 | ], 150 | "name": "texts", 151 | "outputs": [ 152 | { 153 | "internalType": "string", 154 | "name": "text", 155 | "type": "string" 156 | } 157 | ], 158 | "stateMutability": "view", 159 | "type": "function" 160 | }, 161 | { 162 | "inputs": [ 163 | { 164 | "internalType": "uint256", 165 | "name": "commentId", 166 | "type": "uint256" 167 | } 168 | ], 169 | "name": "titles", 170 | "outputs": [ 171 | { 172 | "internalType": "string", 173 | "name": "title", 174 | "type": "string" 175 | } 176 | ], 177 | "stateMutability": "view", 178 | "type": "function" 179 | } 180 | ] -------------------------------------------------------------------------------- /webapp/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "verification-counter", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "vite --open", 9 | "build": "vite build", 10 | "preview": "vite preview", 11 | "relayer": "node relayer/relayer.js" 12 | }, 13 | "author": "", 14 | "license": "ISC", 15 | "dependencies": { 16 | "@noir-lang/backend_barretenberg": "^0.19.4", 17 | "@noir-lang/noir_js": "^0.19.4", 18 | "dotenv": "^16.3.1", 19 | "@alch/alchemy-web3": "^1.1.7", 20 | "cors": "^2.8.5", 21 | "node-fetch": "^3.1.0" 22 | }, 23 | "devDependencies": { 24 | "rollup-plugin-copy": "^3.5.0", 25 | "vite": "^4.5.0", 26 | "vite-plugin-environment": "^1.1.3" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /webapp/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite'; 2 | import copy from 'rollup-plugin-copy'; 3 | import fs from 'fs'; 4 | import path from 'path'; 5 | import EnvironmentPlugin from 'vite-plugin-environment' 6 | 7 | const wasmContentTypePlugin = { 8 | name: 'wasm-content-type-plugin', 9 | configureServer(server) { 10 | server.middlewares.use(async (req, res, next) => { 11 | if (req.url.endsWith('.wasm')) { 12 | res.setHeader('Content-Type', 'application/wasm'); 13 | const newPath = req.url.replace('deps', 'dist'); 14 | const targetPath = path.join(__dirname, newPath); 15 | const wasmContent = fs.readFileSync(targetPath); 16 | return res.end(wasmContent); 17 | } 18 | next(); 19 | }); 20 | }, 21 | }; 22 | 23 | export default defineConfig(({ command }) => { 24 | if (command === 'serve') { 25 | return { 26 | plugins: [ 27 | copy({ 28 | targets: [{ src: 'node_modules/**/*.wasm', dest: 'node_modules/.vite/dist' }], 29 | copySync: true, 30 | hook: 'buildStart', 31 | }), 32 | command === 'serve' ? wasmContentTypePlugin : [], 33 | EnvironmentPlugin(['COMMENT_VERIFIER_ADDRESS','CHAIN_ID']), 34 | ], 35 | }; 36 | } 37 | 38 | return {}; 39 | }); --------------------------------------------------------------------------------