You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

271 lines
9.9 KiB

Add solidity groth16, kzg10 and final decider verifiers in a dedicated workspace (#70) * change: Refactor structure into workspace * chore: Add empty readme * change: Transform repo into workspace * add: Create folding-verifier-solidity crate * add: Include askama.toml for `sol` extension escaper * add: Jordi's old Groth16 verifier .sol template and adapt it * tmp: create simple template struct to test * Update FoldingSchemes trait, fit Nova+CycleFold - update lib.rs's `FoldingScheme` trait interface - fit Nova+CycleFold into the `FoldingScheme` trait - refactor `src/nova/*` * chore: add serialization assets for testing Now we include an `assets` folder with a serialized proof & vk for tests * Add `examples` dir, with Nova's `FoldingScheme` example * polishing * expose poseidon_test_config outside tests * change: Refactor structure into workspace * chore: Add empty readme * change: Transform repo into workspace * add: Create folding-verifier-solidity crate * add: Include askama.toml for `sol` extension escaper * add: Jordi's old Groth16 verifier .sol template and adapt it * tmp: create simple template struct to test * feat: templating kzg working * chore: add emv and revm * feat: start evm file * chore: add ark-poly-commit * chore: move `commitment` to `folding-schemes` * chore: update `.gitignore` to ignore generated contracts * chore: update template with bn254 lib on it (avoids import), update for loop to account for whitespaces * refactor: update template with no lib * feat: add evm deploy code, compile and create kzg verifier * chore: update `Cargo.toml` to have `folding-schemes` available with verifiers * feat: start kzg prove and verify with sol * chore: compute crs from kzg prover * feat: evm kzg verification passing * tmp * change: Swap order of G2 coordinates within the template * Update way to serialize proof with correct order * chore: update `Cargo.toml` * chore: add revm * chore: add `save_solidity` * refactor: verifiers in dedicated mod * refactor: have dedicated `utils` module * chore: expose modules * chore: update verifier for kzg * chore: rename templates * fix: look for binary using also name of contract * refactor: generate groth16 proof for sha256 pre-image, generate groth16 template with verifying key * chore: template renaming * fix: switch circuit for circuit that simply adds * feat: generates test data on the fly * feat: update to latest groth16 verifier * refactor: rename folder, update `.gitignore` * chore: update `Cargo.toml` * chore: update templates extension to indicate that they are templates * chore: rename templates, both files and structs * fix: template inheritance working * feat: template spdx and pragma statements * feat: decider verifier compiles, update test for kzg10 and groth16 templates * feat: parameterize which size of the crs should be stored on the contract * chore: add comment on how the groth16 and kzg10 proofs will be linked together * chore: cargo clippy run * chore: cargo clippy tests * chore: cargo fmt * refactor: remove unused lifetime parameter * chore: end merge * chore: move examples to `folding-schemes` workspace * get latest main changes * fix: temp fix clippy warnings, will remove lints once not used in tests only * fix: cargo clippy lint added on `code_size` * fix: update path to test circuit and add step for installing solc * chore: remove `save_solidity` steps * fix: the borrowed expression implements the required traits * chore: update `Cargo.toml` * chore: remove extra `[patch.crates-io]` * fix: update to patch at the workspace level and add comment explaining this * refactor: correct `staticcall` with valid input/output sizes and change return syntax for pairing * refactor: expose modules and remove `dead_code` calls * chore: update `README.md`, add additional comments on `kzg10` template and update `groth16` template comments * chore: be clearer on attributions on `kzg10` --------- Co-authored-by: CPerezz <c.perezbaro@gmail.com> Co-authored-by: arnaucube <root@arnaucube.com>
7 months ago
add README.md (#39) * Initialize the README.md with a sketch of the structure * add warning and draft diagram * add authors & years to schemes, add a pre-sketch of the 'development' section * Readme: add link to Carlos talk on folding schemes * readme: sketch sections: offchain & onchain decider, add todo for references * readme: add example of FCircuit & folding * Readme: add lib pipeline diagram, add decider code example * add cyclefold-nova-diagram.png, decider-onchain-flow-diagram.png * polish cli descriptions * small update in the Warning box * add sonobe naming * add folding-main-idea-diagram.png * missing sonobe renaming * migrate part of the README.md to sonobe-docs * rm imgs/, load them from sonobe-docs * tiny update * chore: start update README * add acknolwedgments links and text, small polishing of the overall text * extend folding introduction & sonobe overview * img text alignment * chore: update readme * chore: typos, bits of reformulation, centering images * chore: remove btc example since can not be used as is * rm .vscode dir * readme: merge the duplicated sections into a single one adapting the texts * add Docs badge with link, update acknowledgments * add ci & license badges * fix cli link, add solc mention in solidity-verifiers/readme * small polishing * fix img alignment * rm badges, the reasoning is: - The License badge is not needed since there are already many links to the license both in the readme and in the GitHub UI - The CI checks badge, already appears in the GitHub UI in the last commit preview at the main repo page. Furthermore, after some months of inactivity, the badge would be 'gray' as 'inactive'. - The only badge that I was trying to get there is the 'docs' badge, to make it very clear that the docs page exists, but it was a bit to hard visually to have a single badge there, and furthermore the docs link already appears in the readme twice, and also in the GitHub UI right-panel. --------- Co-authored-by: dmpierre <pdaixmoreux@gmail.com>
5 months ago
Add solidity groth16, kzg10 and final decider verifiers in a dedicated workspace (#70) * change: Refactor structure into workspace * chore: Add empty readme * change: Transform repo into workspace * add: Create folding-verifier-solidity crate * add: Include askama.toml for `sol` extension escaper * add: Jordi's old Groth16 verifier .sol template and adapt it * tmp: create simple template struct to test * Update FoldingSchemes trait, fit Nova+CycleFold - update lib.rs's `FoldingScheme` trait interface - fit Nova+CycleFold into the `FoldingScheme` trait - refactor `src/nova/*` * chore: add serialization assets for testing Now we include an `assets` folder with a serialized proof & vk for tests * Add `examples` dir, with Nova's `FoldingScheme` example * polishing * expose poseidon_test_config outside tests * change: Refactor structure into workspace * chore: Add empty readme * change: Transform repo into workspace * add: Create folding-verifier-solidity crate * add: Include askama.toml for `sol` extension escaper * add: Jordi's old Groth16 verifier .sol template and adapt it * tmp: create simple template struct to test * feat: templating kzg working * chore: add emv and revm * feat: start evm file * chore: add ark-poly-commit * chore: move `commitment` to `folding-schemes` * chore: update `.gitignore` to ignore generated contracts * chore: update template with bn254 lib on it (avoids import), update for loop to account for whitespaces * refactor: update template with no lib * feat: add evm deploy code, compile and create kzg verifier * chore: update `Cargo.toml` to have `folding-schemes` available with verifiers * feat: start kzg prove and verify with sol * chore: compute crs from kzg prover * feat: evm kzg verification passing * tmp * change: Swap order of G2 coordinates within the template * Update way to serialize proof with correct order * chore: update `Cargo.toml` * chore: add revm * chore: add `save_solidity` * refactor: verifiers in dedicated mod * refactor: have dedicated `utils` module * chore: expose modules * chore: update verifier for kzg * chore: rename templates * fix: look for binary using also name of contract * refactor: generate groth16 proof for sha256 pre-image, generate groth16 template with verifying key * chore: template renaming * fix: switch circuit for circuit that simply adds * feat: generates test data on the fly * feat: update to latest groth16 verifier * refactor: rename folder, update `.gitignore` * chore: update `Cargo.toml` * chore: update templates extension to indicate that they are templates * chore: rename templates, both files and structs * fix: template inheritance working * feat: template spdx and pragma statements * feat: decider verifier compiles, update test for kzg10 and groth16 templates * feat: parameterize which size of the crs should be stored on the contract * chore: add comment on how the groth16 and kzg10 proofs will be linked together * chore: cargo clippy run * chore: cargo clippy tests * chore: cargo fmt * refactor: remove unused lifetime parameter * chore: end merge * chore: move examples to `folding-schemes` workspace * get latest main changes * fix: temp fix clippy warnings, will remove lints once not used in tests only * fix: cargo clippy lint added on `code_size` * fix: update path to test circuit and add step for installing solc * chore: remove `save_solidity` steps * fix: the borrowed expression implements the required traits * chore: update `Cargo.toml` * chore: remove extra `[patch.crates-io]` * fix: update to patch at the workspace level and add comment explaining this * refactor: correct `staticcall` with valid input/output sizes and change return syntax for pairing * refactor: expose modules and remove `dead_code` calls * chore: update `README.md`, add additional comments on `kzg10` template and update `groth16` template comments * chore: be clearer on attributions on `kzg10` --------- Co-authored-by: CPerezz <c.perezbaro@gmail.com> Co-authored-by: arnaucube <root@arnaucube.com>
7 months ago
Add solidity groth16, kzg10 and final decider verifiers in a dedicated workspace (#70) * change: Refactor structure into workspace * chore: Add empty readme * change: Transform repo into workspace * add: Create folding-verifier-solidity crate * add: Include askama.toml for `sol` extension escaper * add: Jordi's old Groth16 verifier .sol template and adapt it * tmp: create simple template struct to test * Update FoldingSchemes trait, fit Nova+CycleFold - update lib.rs's `FoldingScheme` trait interface - fit Nova+CycleFold into the `FoldingScheme` trait - refactor `src/nova/*` * chore: add serialization assets for testing Now we include an `assets` folder with a serialized proof & vk for tests * Add `examples` dir, with Nova's `FoldingScheme` example * polishing * expose poseidon_test_config outside tests * change: Refactor structure into workspace * chore: Add empty readme * change: Transform repo into workspace * add: Create folding-verifier-solidity crate * add: Include askama.toml for `sol` extension escaper * add: Jordi's old Groth16 verifier .sol template and adapt it * tmp: create simple template struct to test * feat: templating kzg working * chore: add emv and revm * feat: start evm file * chore: add ark-poly-commit * chore: move `commitment` to `folding-schemes` * chore: update `.gitignore` to ignore generated contracts * chore: update template with bn254 lib on it (avoids import), update for loop to account for whitespaces * refactor: update template with no lib * feat: add evm deploy code, compile and create kzg verifier * chore: update `Cargo.toml` to have `folding-schemes` available with verifiers * feat: start kzg prove and verify with sol * chore: compute crs from kzg prover * feat: evm kzg verification passing * tmp * change: Swap order of G2 coordinates within the template * Update way to serialize proof with correct order * chore: update `Cargo.toml` * chore: add revm * chore: add `save_solidity` * refactor: verifiers in dedicated mod * refactor: have dedicated `utils` module * chore: expose modules * chore: update verifier for kzg * chore: rename templates * fix: look for binary using also name of contract * refactor: generate groth16 proof for sha256 pre-image, generate groth16 template with verifying key * chore: template renaming * fix: switch circuit for circuit that simply adds * feat: generates test data on the fly * feat: update to latest groth16 verifier * refactor: rename folder, update `.gitignore` * chore: update `Cargo.toml` * chore: update templates extension to indicate that they are templates * chore: rename templates, both files and structs * fix: template inheritance working * feat: template spdx and pragma statements * feat: decider verifier compiles, update test for kzg10 and groth16 templates * feat: parameterize which size of the crs should be stored on the contract * chore: add comment on how the groth16 and kzg10 proofs will be linked together * chore: cargo clippy run * chore: cargo clippy tests * chore: cargo fmt * refactor: remove unused lifetime parameter * chore: end merge * chore: move examples to `folding-schemes` workspace * get latest main changes * fix: temp fix clippy warnings, will remove lints once not used in tests only * fix: cargo clippy lint added on `code_size` * fix: update path to test circuit and add step for installing solc * chore: remove `save_solidity` steps * fix: the borrowed expression implements the required traits * chore: update `Cargo.toml` * chore: remove extra `[patch.crates-io]` * fix: update to patch at the workspace level and add comment explaining this * refactor: correct `staticcall` with valid input/output sizes and change return syntax for pairing * refactor: expose modules and remove `dead_code` calls * chore: update `README.md`, add additional comments on `kzg10` template and update `groth16` template comments * chore: be clearer on attributions on `kzg10` --------- Co-authored-by: CPerezz <c.perezbaro@gmail.com> Co-authored-by: arnaucube <root@arnaucube.com>
7 months ago
  1. /**
  2. * @author Privacy and Scaling Explorations team - pse.dev
  3. * @dev Contains utility functions for ops in BN254; in G_1 mostly.
  4. * @notice Forked from https://github.com/weijiekoh/libkzg.
  5. * Among others, a few of the changes we did on this fork were:
  6. * - Templating the pragma version
  7. * - Removing type wrappers and use uints instead
  8. * - Performing changes on arg types
  9. * - Update some of the `require` statements
  10. * - Use the bn254 scalar field instead of checking for overflow on the babyjub prime
  11. * - In batch checking, we compute auxiliary polynomials and their commitments at the same time.
  12. */
  13. contract KZG10Verifier {
  14. // prime of field F_p over which y^2 = x^3 + 3 is defined
  15. uint256 public constant BN254_PRIME_FIELD =
  16. 21888242871839275222246405745257275088696311157297823662689037894645226208583;
  17. uint256 public constant BN254_SCALAR_FIELD =
  18. 21888242871839275222246405745257275088548364400416034343698204186575808495617;
  19. /**
  20. * @notice Performs scalar multiplication in G_1.
  21. * @param p G_1 point to multiply
  22. * @param s Scalar to multiply by
  23. * @return r G_1 point p multiplied by scalar s
  24. */
  25. function mulScalar(uint256[2] memory p, uint256 s) internal view returns (uint256[2] memory r) {
  26. uint256[3] memory input;
  27. input[0] = p[0];
  28. input[1] = p[1];
  29. input[2] = s;
  30. bool success;
  31. assembly {
  32. success := staticcall(sub(gas(), 2000), 7, input, 0x60, r, 0x40)
  33. switch success
  34. case 0 { invalid() }
  35. }
  36. require(success, "bn254: scalar mul failed");
  37. }
  38. /**
  39. * @notice Negates a point in G_1.
  40. * @param p G_1 point to negate
  41. * @return uint256[2] G_1 point -p
  42. */
  43. function negate(uint256[2] memory p) internal pure returns (uint256[2] memory) {
  44. if (p[0] == 0 && p[1] == 0) {
  45. return p;
  46. }
  47. return [p[0], BN254_PRIME_FIELD - (p[1] % BN254_PRIME_FIELD)];
  48. }
  49. /**
  50. * @notice Adds two points in G_1.
  51. * @param p1 G_1 point 1
  52. * @param p2 G_1 point 2
  53. * @return r G_1 point p1 + p2
  54. */
  55. function add(uint256[2] memory p1, uint256[2] memory p2) internal view returns (uint256[2] memory r) {
  56. bool success;
  57. uint256[4] memory input = [p1[0], p1[1], p2[0], p2[1]];
  58. assembly {
  59. success := staticcall(sub(gas(), 2000), 6, input, 0x80, r, 0x40)
  60. switch success
  61. case 0 { invalid() }
  62. }
  63. require(success, "bn254: point add failed");
  64. }
  65. /**
  66. * @notice Computes the pairing check e(p1, p2) * e(p3, p4) == 1
  67. * @dev Note that G_2 points a*i + b are encoded as two elements of F_p, (a, b)
  68. * @param a_1 G_1 point 1
  69. * @param a_2 G_2 point 1
  70. * @param b_1 G_1 point 2
  71. * @param b_2 G_2 point 2
  72. * @return result true if pairing check is successful
  73. */
  74. function pairing(uint256[2] memory a_1, uint256[2][2] memory a_2, uint256[2] memory b_1, uint256[2][2] memory b_2)
  75. internal
  76. view
  77. returns (bool result)
  78. {
  79. uint256[12] memory input = [
  80. a_1[0],
  81. a_1[1],
  82. a_2[0][1], // imaginary part first
  83. a_2[0][0],
  84. a_2[1][1], // imaginary part first
  85. a_2[1][0],
  86. b_1[0],
  87. b_1[1],
  88. b_2[0][1], // imaginary part first
  89. b_2[0][0],
  90. b_2[1][1], // imaginary part first
  91. b_2[1][0]
  92. ];
  93. uint256[1] memory out;
  94. bool success;
  95. assembly {
  96. success := staticcall(sub(gas(), 2000), 8, input, 0x180, out, 0x20)
  97. switch success
  98. case 0 { invalid() }
  99. }
  100. require(success, "bn254: pairing failed");
  101. return out[0] == 1;
  102. }
  103. uint256[2] G_1 = [
  104. {{ g1.0[0] }},
  105. {{ g1.0[1] }}
  106. ];
  107. uint256[2][2] G_2 = [
  108. [
  109. {{ g2.0[0][0] }},
  110. {{ g2.0[0][1] }}
  111. ],
  112. [
  113. {{ g2.0[1][0] }},
  114. {{ g2.0[1][1] }}
  115. ]
  116. ];
  117. uint256[2][2] VK = [
  118. [
  119. {{ vk.0[0][0] }},
  120. {{ vk.0[0][1] }}
  121. ],
  122. [
  123. {{ vk.0[1][0] }},
  124. {{ vk.0[1][1] }}
  125. ]
  126. ];
  127. uint256[2][{{ g1_crs_len }}] G1_CRS = [
  128. {%- for (i, point) in g1_crs.iter().enumerate() %}
  129. [
  130. {{ point.0[0] }},
  131. {{ point.0[1] }}
  132. {% if loop.last -%}
  133. ]
  134. {%- else -%}
  135. ],
  136. {%- endif -%}
  137. {% endfor -%}
  138. ];
  139. /**
  140. * @notice Verifies a single point evaluation proof. Function name follows `ark-poly`.
  141. * @dev To avoid ops in G_2, we slightly tweak how the verification is done.
  142. * @param c G_1 point commitment to polynomial.
  143. * @param pi G_1 point proof.
  144. * @param x Value to prove evaluation of polynomial at.
  145. * @param y Evaluation poly(x).
  146. * @return result Indicates if KZG proof is correct.
  147. */
  148. function check(uint256[2] calldata c, uint256[2] calldata pi, uint256 x, uint256 y)
  149. public
  150. view
  151. returns (bool result)
  152. {
  153. //
  154. // we want to:
  155. // 1. avoid gas intensive ops in G2
  156. // 2. format the pairing check in line with what the evm opcode expects.
  157. //
  158. // we can do this by tweaking the KZG check to be:
  159. //
  160. // e(pi, vk - x * g2) = e(c - y * g1, g2) [initial check]
  161. // e(pi, vk - x * g2) * e(c - y * g1, g2)^{-1} = 1
  162. // e(pi, vk - x * g2) * e(-c + y * g1, g2) = 1 [bilinearity of pairing for all subsequent steps]
  163. // e(pi, vk) * e(pi, -x * g2) * e(-c + y * g1, g2) = 1
  164. // e(pi, vk) * e(-x * pi, g2) * e(-c + y * g1, g2) = 1
  165. // e(pi, vk) * e(x * -pi - c + y * g1, g2) = 1 [done]
  166. // |_ rhs_pairing _|
  167. //
  168. uint256[2] memory rhs_pairing =
  169. add(mulScalar(negate(pi), x), add(negate(c), mulScalar(G_1, y)));
  170. return pairing(pi, VK, rhs_pairing, G_2);
  171. }
  172. function evalPolyAt(uint256[] memory _coefficients, uint256 _index) public pure returns (uint256) {
  173. uint256 m = BN254_SCALAR_FIELD;
  174. uint256 result = 0;
  175. uint256 powerOfX = 1;
  176. for (uint256 i = 0; i < _coefficients.length; i++) {
  177. uint256 coeff = _coefficients[i];
  178. assembly {
  179. result := addmod(result, mulmod(powerOfX, coeff, m), m)
  180. powerOfX := mulmod(powerOfX, _index, m)
  181. }
  182. }
  183. return result;
  184. }
  185. /**
  186. * @notice Ensures that z(x) == 0 and l(x) == y for all x in x_vals and y in y_vals. It returns the commitment to z(x) and l(x).
  187. * @param z_coeffs coefficients of the zero polynomial z(x) = (x - x_1)(x - x_2)...(x - x_n).
  188. * @param l_coeffs coefficients of the lagrange polynomial l(x).
  189. * @param x_vals x values to evaluate the polynomials at.
  190. * @param y_vals y values to which l(x) should evaluate to.
  191. * @return uint256[2] commitment to z(x).
  192. * @return uint256[2] commitment to l(x).
  193. */
  194. function checkAndCommitAuxPolys(
  195. uint256[] memory z_coeffs,
  196. uint256[] memory l_coeffs,
  197. uint256[] memory x_vals,
  198. uint256[] memory y_vals
  199. ) public view returns (uint256[2] memory, uint256[2] memory) {
  200. // z(x) is of degree len(x_vals), it is a product of linear polynomials (x - x_i)
  201. // l(x) is of degree len(x_vals) - 1
  202. uint256[2] memory z_commit;
  203. uint256[2] memory l_commit;
  204. for (uint256 i = 0; i < x_vals.length; i++) {
  205. z_commit = add(z_commit, mulScalar(G1_CRS[i], z_coeffs[i])); // update commitment to z(x)
  206. l_commit = add(l_commit, mulScalar(G1_CRS[i], l_coeffs[i])); // update commitment to l(x)
  207. uint256 eval_z = evalPolyAt(z_coeffs, x_vals[i]);
  208. uint256 eval_l = evalPolyAt(l_coeffs, x_vals[i]);
  209. require(eval_z == 0, "checkAndCommitAuxPolys: wrong zero poly");
  210. require(eval_l == y_vals[i], "checkAndCommitAuxPolys: wrong lagrange poly");
  211. }
  212. // z(x) has len(x_vals) + 1 coeffs, we add to the commitment the last coeff of z(x)
  213. z_commit = add(z_commit, mulScalar(G1_CRS[z_coeffs.length - 1], z_coeffs[z_coeffs.length - 1]));
  214. return (z_commit, l_commit);
  215. }
  216. /**
  217. * @notice Verifies a batch of point evaluation proofs. Function name follows `ark-poly`.
  218. * @dev To avoid ops in G_2, we slightly tweak how the verification is done.
  219. * @param c G1 point commitment to polynomial.
  220. * @param pi G2 point proof.
  221. * @param x_vals Values to prove evaluation of polynomial at.
  222. * @param y_vals Evaluation poly(x).
  223. * @param l_coeffs Coefficients of the lagrange polynomial.
  224. * @param z_coeffs Coefficients of the zero polynomial z(x) = (x - x_1)(x - x_2)...(x - x_n).
  225. * @return result Indicates if KZG proof is correct.
  226. */
  227. function batchCheck(
  228. uint256[2] calldata c,
  229. uint256[2][2] calldata pi,
  230. uint256[] calldata x_vals,
  231. uint256[] calldata y_vals,
  232. uint256[] calldata l_coeffs,
  233. uint256[] calldata z_coeffs
  234. ) public view returns (bool result) {
  235. //
  236. // we want to:
  237. // 1. avoid gas intensive ops in G2
  238. // 2. format the pairing check in line with what the evm opcode expects.
  239. //
  240. // we can do this by tweaking the KZG check to be:
  241. //
  242. // e(z(r) * g1, pi) * e(g1, l(r) * g2) = e(c, g2) [initial check]
  243. // e(z(r) * g1, pi) * e(l(r) * g1, g2) * e(c, g2)^{-1} = 1 [bilinearity of pairing]
  244. // e(z(r) * g1, pi) * e(l(r) * g1 - c, g2) = 1 [done]
  245. //
  246. (uint256[2] memory z_commit, uint256[2] memory l_commit) =
  247. checkAndCommitAuxPolys(z_coeffs, l_coeffs, x_vals, y_vals);
  248. uint256[2] memory neg_commit = negate(c);
  249. return pairing(z_commit, pi, add(l_commit, neg_commit), G_2);
  250. }
  251. }