├── .gitignore ├── Cargo.toml ├── LICENSE ├── README.md ├── examples ├── multi_lookup.rs └── single_opening.rs ├── rustfmt.toml └── src ├── dft.rs ├── kzg.rs ├── lib.rs ├── multi ├── mod.rs ├── setup.rs └── unity.rs ├── pedersen.rs ├── single ├── mod.rs ├── setup.rs └── unity.rs ├── transcript.rs └── util.rs /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | /caulk_multi_lookup/target/ 5 | 6 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 7 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 8 | Cargo.lock 9 | 10 | # These are backup files generated by rustfmt 11 | **/*.rs.bk 12 | 13 | srs/ 14 | polys/ 15 | tmp/ 16 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "caulk" 3 | version = "0.1.0" 4 | authors = ["mmaller "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | ark-ff = { version = "^0.3.0", default-features = false } 11 | ark-ec = { version = "^0.3.0", default-features = false } 12 | ark-serialize = { version = "^0.3.0", default-features = false, features = [ "derive" ] } 13 | ark-poly = { version = "^0.3.0", default-features = false } 14 | ark-std = { version = "^0.3.0", default-features = false } 15 | ark-r1cs-std = { version = "^0.3.0", default-features = false, optional = true } 16 | ark-bls12-381 = { version = "^0.3.0", features = [ "std" ] } 17 | ark-bls12-377 = { version = "^0.3.0", features = [ "std" ] } 18 | ark-poly-commit = { version = "^0.3.0", default-features = false } 19 | 20 | tracing = { version = "0.1", default-features = false, features = [ "attributes" ], optional = true } 21 | derivative = { version = "2.0", features = ["use_core"], optional = true} 22 | rand = "0.8.5" 23 | rand_chacha = { version = "0.3.1" } 24 | thiserror = "1.0.19" 25 | blake2s_simd = "1.0.0" 26 | 27 | rayon = { version = "1.5.2", default-features = false, optional = true } 28 | merlin = { version = "3.0.0" } 29 | 30 | [features] 31 | asm = [ "ark-ff/asm" ] 32 | parallel = [ 33 | "rayon", 34 | "ark-std/parallel", 35 | "ark-ff/parallel", 36 | "ark-poly/parallel" 37 | ] 38 | print-trace = [ 39 | "ark-std/print-trace" 40 | ] 41 | 42 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # caulk 2 | Linkable vector commitments and lookup tables. 3 | 4 | WARNING: This project contains a proof of concept implementation of Caulk that has not received any formal audit. It should not be used production. 5 | 6 | This project contains sample SRS and Q-openings (for a sample poly) for various N up to 2^12. If SRS and/or openings don't exist they are recreated (this is timely!). 7 | 8 | SRS and openings for bigger N (up to 2^20) can be downloaded [here](https://drive.google.com/file/d/1ANrNC-aIW22Z6Kx8vrea21Abl2q52hQH/view?usp=sharing). 9 | The folders `polys` and `srs` in the archive should be merged with their counterparts in this project. 10 | -------------------------------------------------------------------------------- /examples/multi_lookup.rs: -------------------------------------------------------------------------------- 1 | use ark_bls12_381::{Bls12_381, Fr}; 2 | use ark_poly::{univariate::DensePolynomial, EvaluationDomain}; 3 | use ark_poly_commit::{Polynomial, UVPolynomial}; 4 | use ark_std::{test_rng, time::Instant, UniformRand}; 5 | use caulk::{ 6 | multi::{ 7 | compute_lookup_proof, get_poly_and_g2_openings, verify_lookup_proof, LookupInstance, 8 | LookupProverInput, 9 | }, 10 | KZGCommit, PublicParameters, 11 | }; 12 | use rand::Rng; 13 | use std::{cmp::max, error::Error, io, str::FromStr}; 14 | 15 | // Function for reading inputs from the command line. 16 | fn read_line() -> T 17 | where 18 | ::Err: Error + 'static, 19 | { 20 | let mut input = String::new(); 21 | io::stdin() 22 | .read_line(&mut input) 23 | .expect("Failed to get console input."); 24 | let output: T = input.trim().parse().expect("Console input is invalid."); 25 | output 26 | } 27 | 28 | #[allow(non_snake_case)] 29 | fn main() { 30 | let mut rng = test_rng(); 31 | 32 | // 1. Setup 33 | // setting public parameters 34 | // current kzg setup should be changed with output from a setup ceremony 35 | println!("What is the bitsize of the degree of the polynomial inside the commitment? "); 36 | let n: usize = read_line(); 37 | println!("How many positions m do you want to open the polynomial at? "); 38 | let m: usize = read_line(); 39 | 40 | let N: usize = 1 << n; 41 | let powers_size: usize = max(N + 2, 1024); 42 | let actual_degree = N - 1; 43 | let temp_m = n; // dummy 44 | 45 | let now = Instant::now(); 46 | let mut pp = PublicParameters::::setup(&powers_size, &N, &temp_m, &n); 47 | println!( 48 | "Time to setup multi openings of table size {:?} = {:?}", 49 | actual_degree + 1, 50 | now.elapsed() 51 | ); 52 | 53 | // 2. Poly and openings 54 | let now = Instant::now(); 55 | let table = get_poly_and_g2_openings(&pp, actual_degree); 56 | println!("Time to generate commitment table = {:?}", now.elapsed()); 57 | 58 | // 3. Setup 59 | 60 | pp.regenerate_lookup_params(m); 61 | 62 | // 4. Positions 63 | // let mut rng = rand::thread_rng(); 64 | let mut positions: Vec = vec![]; 65 | for _ in 0..m { 66 | // generate positions randomly in the set 67 | // let i_j: usize = j*(actual_degree/m); 68 | let i_j: usize = rng.gen_range(0..actual_degree); 69 | positions.push(i_j); 70 | } 71 | 72 | println!("positions = {:?}", positions); 73 | 74 | // 5. generating phi 75 | let blinder = Fr::rand(&mut rng); 76 | let a_m = DensePolynomial::from_coefficients_slice(&[blinder]); 77 | let mut phi_poly = a_m.mul_by_vanishing_poly(pp.domain_m); 78 | let c_poly_local = table.c_poly.clone(); 79 | 80 | for j in 0..m { 81 | phi_poly = &phi_poly 82 | + &(&pp.lagrange_polynomials_m[j] 83 | * c_poly_local.evaluate(&pp.domain_N.element(positions[j]))); // adding c(w^{i_j})*mu_j(X) 84 | } 85 | 86 | for j in m..pp.domain_m.size() { 87 | phi_poly = &phi_poly 88 | + &(&pp.lagrange_polynomials_m[j] * c_poly_local.evaluate(&pp.domain_N.element(0))); 89 | // adding c(w^{i_j})*mu_j(X) 90 | } 91 | 92 | // 6. Running proofs 93 | let now = Instant::now(); 94 | let c_com = KZGCommit::::commit_g1(&pp.poly_ck, &table.c_poly); 95 | let phi_com = KZGCommit::::commit_g1(&pp.poly_ck, &phi_poly); 96 | println!("Time to generate inputs = {:?}", now.elapsed()); 97 | 98 | let lookup_instance = LookupInstance { c_com, phi_com }; 99 | 100 | let prover_input = LookupProverInput { 101 | c_poly: table.c_poly.clone(), 102 | phi_poly, 103 | positions, 104 | openings: table.openings.clone(), 105 | }; 106 | 107 | println!("We are now ready to run the prover. How many times should we run it?"); 108 | let number_of_openings: usize = read_line(); 109 | let now = Instant::now(); 110 | let (proof, unity_proof) = compute_lookup_proof(&lookup_instance, &prover_input, &pp, &mut rng); 111 | for _ in 1..number_of_openings { 112 | _ = compute_lookup_proof(&lookup_instance, &prover_input, &pp, &mut rng); 113 | } 114 | println!( 115 | "Time to evaluate {} times {} multi-openings of table size {:?} = {:?} ", 116 | number_of_openings, 117 | m, 118 | N, 119 | now.elapsed() 120 | ); 121 | 122 | let now = Instant::now(); 123 | for _ in 0..number_of_openings { 124 | verify_lookup_proof(&table.c_com, &phi_com, &proof, &unity_proof, &pp, &mut rng); 125 | } 126 | println!( 127 | "Time to verify {} times {} multi-openings of table size {:?} = {:?} ", 128 | number_of_openings, 129 | m, 130 | N, 131 | now.elapsed() 132 | ); 133 | 134 | assert!( 135 | verify_lookup_proof(&table.c_com, &phi_com, &proof, &unity_proof, &pp, &mut rng), 136 | "Result does not verify" 137 | ); 138 | } 139 | -------------------------------------------------------------------------------- /examples/single_opening.rs: -------------------------------------------------------------------------------- 1 | use ark_bls12_381::{Bls12_381, Fr, G1Affine}; 2 | use ark_ec::{AffineCurve, ProjectiveCurve}; 3 | use ark_poly::{ 4 | univariate::DensePolynomial, EvaluationDomain, GeneralEvaluationDomain, Polynomial, 5 | UVPolynomial, 6 | }; 7 | use ark_poly_commit::kzg10::KZG10; 8 | use ark_std::{test_rng, UniformRand}; 9 | use caulk::{ 10 | caulk_single_prove, caulk_single_setup, caulk_single_verify, CaulkTranscript, KZGCommit, 11 | }; 12 | use std::{error::Error, io, str::FromStr, time::Instant}; 13 | 14 | type UniPoly381 = DensePolynomial; 15 | type KzgBls12_381 = KZG10; 16 | 17 | // Function for reading inputs from the command line. 18 | fn read_line() -> T 19 | where 20 | ::Err: Error + 'static, 21 | { 22 | let mut input = String::new(); 23 | io::stdin() 24 | .read_line(&mut input) 25 | .expect("Failed to get console input."); 26 | let output: T = input.trim().parse().expect("Console input is invalid."); 27 | output 28 | } 29 | 30 | #[allow(non_snake_case)] 31 | fn main() { 32 | let mut rng = test_rng(); 33 | 34 | // setting public parameters 35 | // current kzg setup should be changed with output from a setup ceremony 36 | println!("What is the bitsize of the degree of the polynomial inside the commitment? "); 37 | let p: usize = read_line(); 38 | let max_degree: usize = (1 << p) + 2; 39 | let actual_degree: usize = (1 << p) - 1; 40 | 41 | // run the setup 42 | let now = Instant::now(); 43 | let pp = caulk_single_setup(max_degree, actual_degree, &mut rng); 44 | println!( 45 | "Time to setup single openings of table size {:?} = {:?}", 46 | actual_degree + 1, 47 | now.elapsed() 48 | ); 49 | 50 | // polynomial and commitment 51 | let now = Instant::now(); 52 | // deterministic randomness. Should never be used in practice. 53 | let c_poly = UniPoly381::rand(actual_degree, &mut rng); 54 | let (g1_C, _) = KzgBls12_381::commit(&pp.poly_ck, &c_poly, None, None).unwrap(); 55 | let g1_C = g1_C.0; 56 | println!( 57 | "Time to KZG commit one element from table size {:?} = {:?}", 58 | actual_degree + 1, 59 | now.elapsed() 60 | ); 61 | 62 | // point at which we will open c_com 63 | let input_domain: GeneralEvaluationDomain = EvaluationDomain::new(actual_degree).unwrap(); 64 | println!("Which position in the vector should we open at? "); 65 | let position: usize = read_line(); 66 | assert!(0 < position, "This position does not exist in this vector."); 67 | assert!( 68 | position <= (actual_degree + 1), 69 | "This position does not exist in this vector." 70 | ); 71 | let omega_i = input_domain.element(position); 72 | 73 | // Deciding whether to open all positions or just the one position. 74 | println!("Should we open all possible positions? Opening all possible positions is slow. Please input either YES or NO" ); 75 | let open_all: String = read_line(); 76 | 77 | let g1_q: G1Affine; 78 | if (open_all == "NO") || (open_all == "No") || (open_all == "no") { 79 | // Q = g1_q = g^( (c(x) - c(w_i)) / (x - w_i) ) 80 | let now = Instant::now(); 81 | let a = KZGCommit::open_g1_batch(&pp.poly_ck, &c_poly, None, &[omega_i]); 82 | println!( 83 | "Time to KZG open one element from table size {:?} = {:?}", 84 | actual_degree + 1, 85 | now.elapsed() 86 | ); 87 | g1_q = a.1; 88 | } else { 89 | assert!( 90 | (open_all == "YES") || (open_all == "Yes") || (open_all == "yes"), 91 | "Console input is invalid" 92 | ); 93 | 94 | // compute all openings 95 | let now = Instant::now(); 96 | let g1_qs = 97 | KZGCommit::::multiple_open::(&c_poly, &pp.poly_ck.powers_of_g, p); 98 | g1_q = g1_qs[position]; 99 | println!("Time to compute all KZG openings {:?}", now.elapsed()); 100 | } 101 | 102 | // z = c(w_i) and cm = g^z h^r for random r 103 | let z = c_poly.evaluate(&omega_i); 104 | let r = Fr::rand(&mut rng); 105 | let cm = (pp.verifier_pp.pedersen_param.g.mul(z) + pp.verifier_pp.pedersen_param.h.mul(r)) 106 | .into_affine(); 107 | 108 | let mut prover_transcript = CaulkTranscript::::new(); 109 | let mut verifier_transcript = CaulkTranscript::::new(); 110 | 111 | // run the prover 112 | println!("We are now ready to run the prover. How many times should we run it?"); 113 | let number_of_openings: usize = read_line(); 114 | let now = Instant::now(); 115 | 116 | let mut proof_evaluate = caulk_single_prove( 117 | &pp, 118 | &mut prover_transcript, 119 | &g1_C, 120 | &cm, 121 | position, 122 | &g1_q, 123 | &z, 124 | &r, 125 | &mut rng, 126 | ); 127 | for _ in 1..(number_of_openings - 1) { 128 | proof_evaluate = caulk_single_prove( 129 | &pp, 130 | &mut prover_transcript, 131 | &g1_C, 132 | &cm, 133 | position, 134 | &g1_q, 135 | &z, 136 | &r, 137 | &mut rng, 138 | ); 139 | } 140 | println!( 141 | "Time to evaluate {} single openings of table size {:?} = {:?}", 142 | number_of_openings, 143 | actual_degree + 1, 144 | now.elapsed() 145 | ); 146 | 147 | // run the verifier 148 | println!( 149 | "The proof verifies = {:?}", 150 | caulk_single_verify( 151 | &pp.verifier_pp, 152 | &mut verifier_transcript, 153 | &g1_C, 154 | &cm, 155 | &proof_evaluate, 156 | ) 157 | ); 158 | let now = Instant::now(); 159 | for _ in 0..(number_of_openings - 1) { 160 | caulk_single_verify( 161 | &pp.verifier_pp, 162 | &mut verifier_transcript, 163 | &g1_C, 164 | &cm, 165 | &proof_evaluate, 166 | ); 167 | } 168 | println!( 169 | "Time to verify {} single openings of table size {:?} = {:?}", 170 | number_of_openings, 171 | actual_degree + 1, 172 | now.elapsed() 173 | ); 174 | } 175 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | reorder_imports = true 2 | wrap_comments = true 3 | normalize_comments = true 4 | use_try_shorthand = true 5 | match_block_trailing_comma = true 6 | use_field_init_shorthand = true 7 | edition = "2018" 8 | condense_wildcard_suffixes = true 9 | imports_granularity = "Crate" -------------------------------------------------------------------------------- /src/dft.rs: -------------------------------------------------------------------------------- 1 | // This file includes an algorithm for calculating n openings of a KZG vector 2 | // commitment of size n in n log(n) time. The algorithm is by Feist and 3 | // khovratovich. It is useful for preprocessing. 4 | // The full algorithm is described here https://github.com/khovratovich/Kate/blob/master/Kate_amortized.pdf 5 | 6 | use ark_ec::ProjectiveCurve; 7 | use ark_ff::PrimeField; 8 | use ark_poly::{ 9 | univariate::DensePolynomial, EvaluationDomain, GeneralEvaluationDomain, UVPolynomial, 10 | }; 11 | use ark_std::{end_timer, start_timer}; 12 | use std::vec::Vec; 13 | 14 | // compute all pre-proofs using DFT 15 | // h_i= c_d[x^{d-i-1}]+c_{d-1}[x^{d-i-2}]+c_{d-2}[x^{d-i-3}]+\cdots + 16 | // c_{i+2}[x]+c_{i+1}[1] 17 | pub fn compute_h( 18 | c_poly: &DensePolynomial, /* c(X) degree up to d<2^p , i.e. c_poly has at most d+1 coeffs 19 | * non-zero */ 20 | powers: &[G], // SRS 21 | p: usize, 22 | ) -> Vec 23 | where 24 | F: PrimeField, 25 | G: ProjectiveCurve, 26 | { 27 | let timer = start_timer!(|| "compute h"); 28 | let mut coeffs = c_poly.coeffs().to_vec(); 29 | let dom_size = 1 << p; 30 | let fpzero = F::zero(); 31 | coeffs.resize(dom_size, fpzero); 32 | 33 | // 1. x_ext = [[x^(d-1)], [x^{d-2},...,[x],[1], d+2 [0]'s] 34 | let step1_timer = start_timer!(|| "step 1"); 35 | let mut x_ext: Vec = powers.iter().take(dom_size - 1).rev().copied().collect(); 36 | x_ext.resize(2 * dom_size, G::zero()); // filling 2d+2 neutral elements 37 | let y = group_dft::(&x_ext, p + 1); 38 | end_timer!(step1_timer); 39 | 40 | // 2. c_ext = [c_d, d zeroes, c_d,c_{0},c_1,...,c_{d-2},c_{d-1}] 41 | let step2_timer = start_timer!(|| "step 2"); 42 | 43 | let mut c_ext = vec![coeffs[coeffs.len() - 1]]; 44 | c_ext.resize(dom_size, fpzero); 45 | c_ext.push(coeffs[coeffs.len() - 1]); 46 | for &e in coeffs.iter().take(coeffs.len() - 1) { 47 | c_ext.push(e); 48 | } 49 | assert_eq!(c_ext.len(), 2 * dom_size); 50 | let v = field_dft::(&c_ext, p + 1); 51 | end_timer!(step2_timer); 52 | 53 | // 3. u = y o v 54 | let step3_timer = start_timer!(|| "step 3"); 55 | let u: Vec<_> = y 56 | .into_iter() 57 | .zip(v.into_iter()) 58 | .map(|(a, b)| a.mul(b.into_repr())) 59 | .collect(); 60 | end_timer!(step3_timer); 61 | 62 | // 4. h_ext = idft_{2d+2}(u) 63 | let step4_timer = start_timer!(|| "step 4"); 64 | let h_ext = group_inv_dft::(&u, p + 1); 65 | end_timer!(step4_timer); 66 | 67 | end_timer!(timer); 68 | h_ext[0..dom_size].to_vec() 69 | } 70 | 71 | // compute DFT of size @dom_size over vector of Fr elements 72 | // q_i = h_0 + h_1w^i + h_2w^{2i}+\cdots + h_{dom_size-1}w^{(dom_size-1)i} for 73 | // 0<= i< dom_size=2^p 74 | pub fn group_dft(h: &[G], p: usize) -> Vec 75 | where 76 | F: PrimeField, 77 | G: ProjectiveCurve, 78 | { 79 | let dom_size = 1 << p; 80 | let timer = start_timer!(|| format!("size {} group dft", dom_size)); 81 | assert_eq!(h.len(), dom_size); // we do not support inputs of size not power of 2 82 | let input_domain: GeneralEvaluationDomain = EvaluationDomain::new(dom_size).unwrap(); 83 | let mut l = dom_size / 2; 84 | let mut m: usize = 1; 85 | // Stockham FFT 86 | let mut xvec = h.to_vec(); 87 | for _ in 0..p { 88 | let mut xt = xvec.clone(); 89 | for j in 0..l { 90 | for k in 0..m { 91 | let c0 = xvec[k + j * m]; 92 | let c1 = xvec[k + j * m + l * m]; 93 | xt[k + 2 * j * m] = c0 + c1; 94 | let wj_2l = input_domain.element((j * dom_size / (2 * l)) % dom_size); 95 | xt[k + 2 * j * m + m] = (c0 - c1).mul(wj_2l.into_repr()); 96 | } 97 | } 98 | l /= 2; 99 | m *= 2; 100 | xvec = xt; 101 | } 102 | end_timer!(timer); 103 | xvec 104 | } 105 | 106 | // compute DFT of size @dom_size over vector of Fr elements 107 | // q_i = h_0 + h_1w^i + h_2w^{2i}+\cdots + h_{dom_size-1}w^{(dom_size-1)i} for 108 | // 0<= i< dom_size=2^p 109 | pub fn field_dft(h: &[F], p: usize) -> Vec { 110 | let dom_size = 1 << p; 111 | let timer = start_timer!(|| format!("size {} field dft", dom_size)); 112 | assert_eq!(h.len(), dom_size); // we do not support inputs of size not power of 2 113 | let input_domain: GeneralEvaluationDomain = EvaluationDomain::new(dom_size).unwrap(); 114 | let mut l = dom_size / 2; 115 | let mut m: usize = 1; 116 | // Stockham FFT 117 | let mut xvec = h.to_vec(); 118 | for _ in 0..p { 119 | let mut xt = xvec.clone(); 120 | for j in 0..l { 121 | for k in 0..m { 122 | let c0 = xvec[k + j * m]; 123 | let c1 = xvec[k + j * m + l * m]; 124 | xt[k + 2 * j * m] = c0 + c1; 125 | let wj_2l = input_domain.element((j * dom_size / (2 * l)) % dom_size); 126 | xt[k + 2 * j * m + m] = (c0 - c1) * (wj_2l); 127 | } 128 | } 129 | l /= 2; 130 | m *= 2; 131 | xvec = xt; 132 | } 133 | end_timer!(timer); 134 | xvec 135 | } 136 | 137 | // compute idft of size @dom_size over vector of G1 elements 138 | // q_i = (h_0 + h_1w^-i + h_2w^{-2i}+\cdots + 139 | // h_{dom_size-1}w^{-(dom_size-1)i})/dom_size for 0<= i< dom_size=2^p 140 | pub fn group_inv_dft(h: &[G], p: usize) -> Vec 141 | where 142 | F: PrimeField, 143 | G: ProjectiveCurve, 144 | { 145 | let dom_size = 1 << p; 146 | let timer = start_timer!(|| format!("size {} group inverse dft", dom_size)); 147 | assert_eq!(h.len(), dom_size); // we do not support inputs of size not power of 2 148 | let input_domain: GeneralEvaluationDomain = EvaluationDomain::new(dom_size).unwrap(); 149 | let mut l = dom_size / 2; 150 | let mut m: usize = 1; 151 | // Stockham FFT 152 | let mut xvec = h.to_vec(); 153 | for _ in 0..p { 154 | let mut xt = xvec.clone(); 155 | for j in 0..l { 156 | for k in 0..m { 157 | let c0 = xvec[k + j * m]; 158 | let c1 = xvec[k + j * m + l * m]; 159 | xt[k + 2 * j * m] = c0 + c1; 160 | let wj_2l = input_domain 161 | .element((dom_size - (j * dom_size / (2 * l)) % dom_size) % dom_size); 162 | xt[k + 2 * j * m + m] = (c0 - c1).mul(wj_2l.into_repr()); // Difference #1 to forward DFT 163 | } 164 | } 165 | l /= 2; 166 | m *= 2; 167 | xvec = xt; 168 | } 169 | 170 | let domain_inverse = F::from(1u64 << p).inverse().unwrap().into_repr(); 171 | let res = xvec.iter().map(|x| x.mul(domain_inverse)).collect(); 172 | 173 | end_timer!(timer); 174 | res 175 | } 176 | 177 | // compute idft of size @dom_size over vector of G1 elements 178 | // q_i = (h_0 + h_1w^-i + h_2w^{-2i}+\cdots + 179 | // h_{dom_size-1}w^{-(dom_size-1)i})/dom_size for 0<= i< dom_size=2^p 180 | pub fn field_inv_dft(h: &[F], p: usize) -> Vec { 181 | let dom_size = 1 << p; 182 | let timer = start_timer!(|| format!("size {} field inverse dft", dom_size)); 183 | assert_eq!(h.len(), dom_size); // we do not support inputs of size not power of 2 184 | let input_domain: GeneralEvaluationDomain = EvaluationDomain::new(dom_size).unwrap(); 185 | let mut l = dom_size / 2; 186 | let mut m: usize = 1; 187 | // Stockham FFT 188 | let mut xvec = h.to_vec(); 189 | for _ in 0..p { 190 | let mut xt = xvec.clone(); 191 | for j in 0..l { 192 | for k in 0..m { 193 | let c0 = xvec[k + j * m]; 194 | let c1 = xvec[k + j * m + l * m]; 195 | xt[k + 2 * j * m] = c0 + c1; 196 | let wj_2l = input_domain 197 | .element((dom_size - (j * dom_size / (2 * l)) % dom_size) % dom_size); 198 | xt[k + 2 * j * m + m] = (c0 - c1) * wj_2l; // Difference #1 to 199 | // forward DFT 200 | } 201 | } 202 | l /= 2; 203 | m *= 2; 204 | xvec = xt; 205 | } 206 | 207 | let domain_inverse = F::from(1u64 << p).inverse().unwrap(); 208 | let res = xvec.iter().map(|&x| x * domain_inverse).collect(); 209 | 210 | end_timer!(timer); 211 | res 212 | } 213 | 214 | #[cfg(test)] 215 | pub mod tests { 216 | use super::*; 217 | use ark_bls12_377::Bls12_377; 218 | use ark_bls12_381::Bls12_381; 219 | use ark_ec::PairingEngine; 220 | use ark_std::{test_rng, UniformRand}; 221 | 222 | #[test] 223 | fn test_dft() { 224 | test_dft_helper::(); 225 | test_dft_helper::(); 226 | } 227 | 228 | fn test_dft_helper() { 229 | let mut rng = test_rng(); 230 | for i in 2..6 { 231 | let size = 1 << i; 232 | 233 | let h: Vec = (0..size).map(|_| E::Fr::rand(&mut rng)).collect(); 234 | 235 | let c_dft = field_dft::(&h, i); 236 | let c_back = field_inv_dft::(&c_dft, i); 237 | assert_eq!(h, c_back); 238 | 239 | let h: Vec = 240 | (0..size).map(|_| E::G1Projective::rand(&mut rng)).collect(); 241 | 242 | let c_dft = group_dft::(&h, i); 243 | let c_back = group_inv_dft::(&c_dft, i); 244 | assert_eq!(h, c_back); 245 | 246 | let h: Vec = 247 | (0..size).map(|_| E::G2Projective::rand(&mut rng)).collect(); 248 | 249 | let c_dft = group_dft::(&h, i); 250 | let c_back = group_inv_dft::(&c_dft, i); 251 | assert_eq!(h, c_back); 252 | } 253 | } 254 | } 255 | -------------------------------------------------------------------------------- /src/kzg.rs: -------------------------------------------------------------------------------- 1 | // This file includes backend tools: 2 | // (1) read_line() is for taking inputs from the user 3 | // (2) kzg_open_g1 is for opening KZG commitments 4 | // (3) kzg_verify_g1 is for verifying KZG commitments 5 | // (4) hash_caulk_single is for hashing group and field elements into a field 6 | // element (5) random_field is for generating random field elements 7 | 8 | use crate::{compute_h, group_dft, util::convert_to_bigints}; 9 | use ark_ec::{msm::VariableBaseMSM, AffineCurve, PairingEngine, ProjectiveCurve}; 10 | use ark_ff::{Field, PrimeField}; 11 | use ark_poly::{ 12 | univariate::DensePolynomial, EvaluationDomain, GeneralEvaluationDomain, Polynomial, 13 | UVPolynomial, 14 | }; 15 | use ark_poly_commit::kzg10::*; 16 | use ark_std::{end_timer, start_timer, One, Zero}; 17 | use std::marker::PhantomData; 18 | 19 | ///////////////////////////////////////////////////////////////////// 20 | // KZG opening and verifying 21 | ///////////////////////////////////////////////////////////////////// 22 | 23 | pub struct KZGCommit { 24 | phantom: PhantomData, 25 | } 26 | 27 | impl KZGCommit { 28 | pub fn commit_g1(powers: &Powers, polynomial: &DensePolynomial) -> E::G1Affine { 29 | let timer = start_timer!(|| "kzg g1 commit"); 30 | let (com, _randomness) = KZG10::::commit(powers, polynomial, None, None).unwrap(); 31 | end_timer!(timer); 32 | com.0 33 | } 34 | 35 | pub fn commit_g2(g2_powers: &[E::G2Affine], poly: &DensePolynomial) -> E::G2Affine { 36 | let timer = start_timer!(|| "kzg g2 commit"); 37 | let poly_coeffs: Vec<::BigInt> = 38 | poly.coeffs.iter().map(|&x| x.into_repr()).collect(); 39 | let res = VariableBaseMSM::multi_scalar_mul(g2_powers, &poly_coeffs).into_affine(); 40 | 41 | end_timer!(timer); 42 | res 43 | } 44 | 45 | // Function to commit to f(X,Y) 46 | // here f = [ [a0, a1, a2], [b1, b2, b3] ] represents (a0 + a1 Y + a2 Y^2 ) + X 47 | // (b1 + b2 Y + b3 Y^2) 48 | // 49 | // First we unwrap to get a vector of form [a0, a1, a2, b0, b1, b2] 50 | // Then we commit to f as a commitment to f'(X) = a0 + a1 X + a2 X^2 + b0 X^3 + 51 | // b1 X^4 + b2 X^5 52 | // 53 | // We also need to know the maximum degree of (a0 + a1 Y + a2 Y^2 ) to prevent 54 | // overflow errors. 55 | // 56 | // This is described in Section 4.6.2 57 | pub fn bipoly_commit( 58 | pp: &crate::multi::PublicParameters, 59 | polys: &[DensePolynomial], 60 | deg_x: usize, 61 | ) -> E::G1Affine { 62 | let timer = start_timer!(|| "kzg bipoly commit"); 63 | let mut poly_formatted = Vec::new(); 64 | 65 | for poly in polys { 66 | let temp = convert_to_bigints(&poly.coeffs); 67 | poly_formatted.extend_from_slice(&temp); 68 | for _ in poly.len()..deg_x { 69 | poly_formatted.push(E::Fr::zero().into_repr()); 70 | } 71 | } 72 | 73 | assert!(pp.poly_ck.powers_of_g.len() >= poly_formatted.len()); 74 | let g1_poly = 75 | VariableBaseMSM::multi_scalar_mul(&pp.poly_ck.powers_of_g, poly_formatted.as_slice()) 76 | .into_affine(); 77 | 78 | end_timer!(timer); 79 | g1_poly 80 | } 81 | 82 | // compute all openings to c_poly using a smart formula 83 | // This Code implements an algorithm for calculating n openings of a KZG vector 84 | // commitment of size n in n log(n) time. The algorithm is by Feist and 85 | // Khovratovich. It is useful for preprocessing. 86 | // The full algorithm is described here https://github.com/khovratovich/Kate/blob/master/Kate_amortized.pdf 87 | pub fn multiple_open( 88 | c_poly: &DensePolynomial, // c(X) 89 | powers: &[G], // SRS 90 | p: usize, 91 | ) -> Vec 92 | where 93 | G: AffineCurve + Sized, 94 | { 95 | let timer = start_timer!(|| "multiple open"); 96 | 97 | let degree = c_poly.coeffs.len() - 1; 98 | let input_domain: GeneralEvaluationDomain = EvaluationDomain::new(degree).unwrap(); 99 | 100 | let h_timer = start_timer!(|| "compute h"); 101 | let powers: Vec = powers.iter().map(|x| x.into_projective()).collect(); 102 | let h2 = compute_h(c_poly, &powers, p); 103 | end_timer!(h_timer); 104 | 105 | let dom_size = input_domain.size(); 106 | assert_eq!(1 << p, dom_size); 107 | assert_eq!(degree + 1, dom_size); 108 | 109 | let dft_timer = start_timer!(|| "G1 dft"); 110 | let q2 = group_dft::(&h2, p); 111 | end_timer!(dft_timer); 112 | 113 | let normalization_timer = start_timer!(|| "batch normalization"); 114 | let res = G::Projective::batch_normalization_into_affine(q2.as_ref()); 115 | end_timer!(normalization_timer); 116 | 117 | end_timer!(timer); 118 | res 119 | } 120 | 121 | //////////////////////////////////////////////// 122 | // KZG.Open( srs_KZG, f(X, Y), deg, alpha ) 123 | // returns ([f(alpha, x)]_1, pi) 124 | // Algorithm described in Section 4.6.2, KZG for Bivariate Polynomials 125 | pub fn partial_open_g1( 126 | pp: &crate::multi::PublicParameters, 127 | polys: &[DensePolynomial], 128 | deg_x: usize, 129 | point: &E::Fr, 130 | ) -> (E::G1Affine, E::G1Affine, DensePolynomial) { 131 | let timer = start_timer!(|| "kzg partial open g1"); 132 | let mut poly_partial_eval = DensePolynomial::from_coefficients_vec(vec![E::Fr::zero()]); 133 | let mut alpha = E::Fr::one(); 134 | for coeff in polys { 135 | let pow_alpha = DensePolynomial::from_coefficients_vec(vec![alpha]); 136 | poly_partial_eval += &(&pow_alpha * coeff); 137 | alpha *= point; 138 | } 139 | 140 | let eval = VariableBaseMSM::multi_scalar_mul( 141 | &pp.poly_ck.powers_of_g, 142 | convert_to_bigints(&poly_partial_eval.coeffs).as_slice(), 143 | ) 144 | .into_affine(); 145 | 146 | let mut witness_bipolynomial = Vec::new(); 147 | let poly_reverse: Vec<_> = polys.iter().rev().collect(); 148 | witness_bipolynomial.push(poly_reverse[0].clone()); 149 | 150 | let alpha = DensePolynomial::from_coefficients_vec(vec![*point]); 151 | for i in 1..(poly_reverse.len() - 1) { 152 | witness_bipolynomial.push(poly_reverse[i] + &(&alpha * &witness_bipolynomial[i - 1])); 153 | } 154 | 155 | witness_bipolynomial.reverse(); 156 | 157 | let proof = Self::bipoly_commit(pp, &witness_bipolynomial, deg_x); 158 | 159 | end_timer!(timer); 160 | (eval, proof, poly_partial_eval) 161 | } 162 | 163 | // KZG.Open( srs_KZG, f(X), deg, (alpha1, alpha2, ..., alphan) ) 164 | // returns ([f(alpha1), ..., f(alphan)], pi) 165 | // Algorithm described in Section 4.6.1, Multiple Openings 166 | pub fn open_g1_batch( 167 | poly_ck: &Powers, 168 | poly: &DensePolynomial, 169 | max_deg: Option<&usize>, 170 | points: &[E::Fr], 171 | ) -> (Vec, E::G1Affine) { 172 | let timer = start_timer!(|| "kzg batch open g1"); 173 | let mut evals = Vec::new(); 174 | let mut proofs = Vec::new(); 175 | for p in points.iter() { 176 | let (eval, pi) = Self::open_g1_single(poly_ck, poly, max_deg, p); 177 | evals.push(eval); 178 | proofs.push(pi); 179 | } 180 | 181 | let mut res = E::G1Projective::zero(); // default value 182 | 183 | for j in 0..points.len() { 184 | let w_j = points[j]; 185 | // 1. Computing coefficient [1/prod] 186 | let mut prod = E::Fr::one(); 187 | for (k, p) in points.iter().enumerate() { 188 | if k != j { 189 | prod *= w_j - p; 190 | } 191 | } 192 | // 2. Summation 193 | let q_add = proofs[j].mul(prod.inverse().unwrap()); //[1/prod]Q_{j} 194 | res += q_add; 195 | } 196 | 197 | end_timer!(timer); 198 | (evals, res.into_affine()) 199 | } 200 | 201 | // KZG.Open( srs_KZG, f(X), deg, alpha ) returns (f(alpha), pi) 202 | fn open_g1_single( 203 | poly_ck: &Powers, 204 | poly: &DensePolynomial, 205 | max_deg: Option<&usize>, 206 | point: &E::Fr, 207 | ) -> (E::Fr, E::G1Affine) { 208 | let timer = start_timer!(|| "kzg open g1"); 209 | let eval = poly.evaluate(point); 210 | 211 | let global_max_deg = poly_ck.powers_of_g.len(); 212 | 213 | let mut d: usize = 0; 214 | if max_deg == None { 215 | d += global_max_deg; 216 | } else { 217 | d += max_deg.unwrap(); 218 | } 219 | let divisor = DensePolynomial::from_coefficients_vec(vec![-*point, E::Fr::one()]); 220 | let witness_polynomial = poly / &divisor; 221 | 222 | assert!(poly_ck.powers_of_g[(global_max_deg - d)..].len() >= witness_polynomial.len()); 223 | let proof = VariableBaseMSM::multi_scalar_mul( 224 | &poly_ck.powers_of_g[(global_max_deg - d)..], 225 | convert_to_bigints(&witness_polynomial.coeffs).as_slice(), 226 | ) 227 | .into_affine(); 228 | 229 | end_timer!(timer); 230 | (eval, proof) 231 | } 232 | 233 | // KZG.Verify( srs_KZG, F, deg, (alpha1, alpha2, ..., alphan), (v1, ..., vn), pi 234 | // ) Algorithm described in Section 4.6.1, Multiple Openings 235 | pub fn verify_g1( 236 | // Verify that @c_com is a commitment to C(X) such that C(x)=z 237 | powers_of_g1: &[E::G1Affine], // generator of G1 238 | powers_of_g2: &[E::G2Affine], // [1]_2, [x]_2, [x^2]_2, ... 239 | c_com: &E::G1Affine, // commitment 240 | max_deg: Option<&usize>, // max degree 241 | points: &[E::Fr], // x such that eval = C(x) 242 | evals: &[E::Fr], // evaluation 243 | pi: &E::G1Affine, // proof 244 | ) -> bool { 245 | let timer = start_timer!(|| "kzg verify g1"); 246 | let pairing_inputs = Self::verify_g1_defer_pairing( 247 | powers_of_g1, 248 | powers_of_g2, 249 | c_com, 250 | max_deg, 251 | points, 252 | evals, 253 | pi, 254 | ); 255 | 256 | let pairing_timer = start_timer!(|| "pairing product"); 257 | let prepared_pairing_inputs = vec![ 258 | ( 259 | E::G1Prepared::from(pairing_inputs[0].0.into_affine()), 260 | E::G2Prepared::from(pairing_inputs[0].1.into_affine()), 261 | ), 262 | ( 263 | E::G1Prepared::from(pairing_inputs[1].0.into_affine()), 264 | E::G2Prepared::from(pairing_inputs[1].1.into_affine()), 265 | ), 266 | ]; 267 | let res = E::product_of_pairings(prepared_pairing_inputs.iter()).is_one(); 268 | 269 | end_timer!(pairing_timer); 270 | end_timer!(timer); 271 | res 272 | } 273 | 274 | // KZG.Verify( srs_KZG, F, deg, (alpha1, alpha2, ..., alphan), (v1, ..., vn), pi 275 | // ) Algorithm described in Section 4.6.1, Multiple Openings 276 | pub fn verify_g1_defer_pairing( 277 | // Verify that @c_com is a commitment to C(X) such that C(x)=z 278 | powers_of_g1: &[E::G1Affine], // generator of G1 279 | powers_of_g2: &[E::G2Affine], // [1]_2, [x]_2, [x^2]_2, ... 280 | c_com: &E::G1Affine, // commitment 281 | max_deg: Option<&usize>, // max degree 282 | points: &[E::Fr], // x such that eval = C(x) 283 | evals: &[E::Fr], // evaluation 284 | pi: &E::G1Affine, // proof 285 | ) -> Vec<(E::G1Projective, E::G2Projective)> { 286 | let timer = start_timer!(|| "kzg verify g1 (deferring pairing)"); 287 | 288 | // Interpolation set 289 | // tau_i(X) = lagrange_tau[i] = polynomial equal to 0 at point[j] for j!= i and 290 | // 1 at points[i] 291 | 292 | let mut lagrange_tau = DensePolynomial::from_coefficients_slice(&[E::Fr::zero()]); 293 | let mut prod = DensePolynomial::from_coefficients_slice(&[E::Fr::one()]); 294 | let mut components = vec![]; 295 | for &p in points.iter() { 296 | let poly = DensePolynomial::from_coefficients_slice(&[-p, E::Fr::one()]); 297 | prod = &prod * (&poly); 298 | components.push(poly); 299 | } 300 | 301 | for i in 0..points.len() { 302 | let mut temp = &prod / &components[i]; 303 | let lagrange_scalar = temp.evaluate(&points[i]).inverse().unwrap() * evals[i]; 304 | temp.coeffs.iter_mut().for_each(|x| *x *= lagrange_scalar); 305 | lagrange_tau = lagrange_tau + temp; 306 | } 307 | 308 | // commit to sum evals[i] tau_i(X) 309 | assert!( 310 | powers_of_g1.len() >= lagrange_tau.len(), 311 | "KZG verifier doesn't have enough g1 powers" 312 | ); 313 | let g1_tau = VariableBaseMSM::multi_scalar_mul( 314 | &powers_of_g1[..lagrange_tau.len()], 315 | convert_to_bigints(&lagrange_tau.coeffs).as_slice(), 316 | ); 317 | 318 | // vanishing polynomial 319 | let z_tau = prod; 320 | 321 | // commit to z_tau(X) in g2 322 | assert!( 323 | powers_of_g2.len() >= z_tau.len(), 324 | "KZG verifier doesn't have enough g2 powers" 325 | ); 326 | let g2_z_tau = VariableBaseMSM::multi_scalar_mul( 327 | &powers_of_g2[..z_tau.len()], 328 | convert_to_bigints(&z_tau.coeffs).as_slice(), 329 | ); 330 | 331 | let global_max_deg = powers_of_g1.len(); 332 | 333 | let mut d: usize = 0; 334 | if max_deg == None { 335 | d += global_max_deg; 336 | } else { 337 | d += max_deg.unwrap(); 338 | } 339 | 340 | let res = vec![ 341 | ( 342 | g1_tau - c_com.into_projective(), 343 | powers_of_g2[global_max_deg - d].into_projective(), 344 | ), 345 | (pi.into_projective(), g2_z_tau), 346 | ]; 347 | 348 | end_timer!(timer); 349 | res 350 | } 351 | 352 | // KZG.Verify( srs_KZG, F, deg, alpha, F_alpha, pi ) 353 | // Algorithm described in Section 4.6.2, KZG for Bivariate Polynomials 354 | // Be very careful here. Verification is only valid if it is paired with a 355 | // degree check. 356 | pub fn partial_verify_g1( 357 | srs: &crate::multi::PublicParameters, 358 | c_com: &E::G1Affine, // commitment 359 | deg_x: usize, 360 | point: &E::Fr, 361 | partial_eval: &E::G1Affine, 362 | pi: &E::G1Affine, // proof 363 | ) -> bool { 364 | let timer = start_timer!(|| "kzg partial verify g1"); 365 | let pairing_inputs = 366 | Self::partial_verify_g1_defer_pairing(srs, c_com, deg_x, point, partial_eval, pi); 367 | let pairing_timer = start_timer!(|| "pairing product"); 368 | let prepared_pairing_inputs = vec![ 369 | ( 370 | E::G1Prepared::from(pairing_inputs[0].0.into_affine()), 371 | E::G2Prepared::from(pairing_inputs[0].1.into_affine()), 372 | ), 373 | ( 374 | E::G1Prepared::from(pairing_inputs[1].0.into_affine()), 375 | E::G2Prepared::from(pairing_inputs[1].1.into_affine()), 376 | ), 377 | ]; 378 | 379 | let res = E::product_of_pairings(prepared_pairing_inputs.iter()).is_one(); 380 | 381 | end_timer!(pairing_timer); 382 | end_timer!(timer); 383 | res 384 | } 385 | 386 | // KZG.Verify( srs_KZG, F, deg, alpha, F_alpha, pi ) 387 | // Algorithm described in Section 4.6.2, KZG for Bivariate Polynomials 388 | // Be very careful here. Verification is only valid if it is paired with a 389 | // degree check. 390 | pub fn partial_verify_g1_defer_pairing( 391 | srs: &crate::multi::PublicParameters, 392 | c_com: &E::G1Affine, // commitment 393 | deg_x: usize, 394 | point: &E::Fr, 395 | partial_eval: &E::G1Affine, 396 | pi: &E::G1Affine, // proof 397 | ) -> Vec<(E::G1Projective, E::G2Projective)> { 398 | let timer = start_timer!(|| "kzg partial verify g1 (deferring pairing)"); 399 | let res = vec![ 400 | ( 401 | partial_eval.into_projective() - c_com.into_projective(), 402 | srs.g2_powers[0].into_projective(), 403 | ), 404 | ( 405 | pi.into_projective(), 406 | srs.g2_powers[deg_x].into_projective() - srs.g2_powers[0].mul(*point), 407 | ), 408 | ]; 409 | end_timer!(timer); 410 | res 411 | } 412 | 413 | // Algorithm for aggregating KZG proofs into a single proof 414 | // Described in Section 4.6.3 Subset openings 415 | // compute Q =\sum_{j=1}^m \frac{Q_{i_j}}}{\prod_{1\leq k\leq m,\; k\neq 416 | // j}(\omega^{i_j}-\omega^{i_k})} 417 | pub fn aggregate_proof_g2( 418 | openings: &[E::G2Affine], // Q_i 419 | positions: &[usize], // i_j 420 | input_domain: &GeneralEvaluationDomain, 421 | ) -> E::G2Affine { 422 | let timer = start_timer!(|| "kzg aggregate proof"); 423 | 424 | let m = positions.len(); 425 | let mut res = openings[0].into_projective(); // default value 426 | 427 | for j in 0..m { 428 | let i_j = positions[j]; 429 | let w_ij = input_domain.element(i_j); 430 | // 1. Computing coefficient [1/prod] 431 | let mut prod = E::Fr::one(); 432 | for (k, &pos) in positions.iter().enumerate().take(m) { 433 | let w_ik = input_domain.element(pos); 434 | if k != j { 435 | prod *= w_ij - w_ik; 436 | } 437 | } 438 | // 2. Summation 439 | let q_add = openings[i_j].mul(prod.inverse().unwrap()); //[1/prod]Q_{j} 440 | if j == 0 { 441 | res = q_add; 442 | } else { 443 | res += q_add; 444 | } 445 | } 446 | let res = res.into_affine(); 447 | end_timer!(timer); 448 | res 449 | } 450 | } 451 | 452 | pub fn generate_lagrange_polynomials_subset( 453 | positions: &[usize], 454 | srs: &crate::multi::PublicParameters, 455 | ) -> Vec> { 456 | let timer = start_timer!(|| "generate lagrange poly subset"); 457 | 458 | let mut tau_polys = vec![]; 459 | let m = positions.len(); 460 | for j in 0..m { 461 | let mut tau_j = DensePolynomial::from_coefficients_slice(&[E::Fr::one()]); // start from tau_j =1 462 | for k in 0..m { 463 | if k != j { 464 | // tau_j = prod_{k\neq j} (X-w^(i_k))/(w^(i_j)-w^(i_k)) 465 | let denum = srs.domain_N.element(positions[j]) - srs.domain_N.element(positions[k]); 466 | let denum = E::Fr::one() / denum; 467 | tau_j = &tau_j 468 | * &DensePolynomial::from_coefficients_slice(&[ 469 | -srs.domain_N.element(positions[k]) * denum, //-w^(i_k))/(w^(i_j)-w^(i_k) 470 | denum, // 1//(w^(i_j)-w^(i_k)) 471 | ]); 472 | } 473 | } 474 | tau_polys.push(tau_j.clone()); 475 | } 476 | end_timer!(timer); 477 | tau_polys 478 | } 479 | 480 | #[cfg(test)] 481 | pub mod tests { 482 | 483 | use super::{generate_lagrange_polynomials_subset, KZGCommit, *}; 484 | use crate::caulk_single_setup; 485 | use ark_bls12_377::Bls12_377; 486 | use ark_bls12_381::Bls12_381; 487 | use ark_ec::{AffineCurve, PairingEngine, ProjectiveCurve}; 488 | use ark_poly::{univariate::DensePolynomial, EvaluationDomain, Polynomial, UVPolynomial}; 489 | use ark_poly_commit::kzg10::KZG10; 490 | use ark_std::{test_rng, One, Zero}; 491 | use std::time::Instant; 492 | 493 | #[test] 494 | fn test_lagrange() { 495 | test_lagrange_helper::(); 496 | test_lagrange_helper::(); 497 | } 498 | 499 | #[allow(non_snake_case)] 500 | fn test_lagrange_helper() { 501 | let p: usize = 8; // bitlength of poly degree 502 | let max_degree: usize = (1 << p) + 2; 503 | let m: usize = 8; 504 | let N: usize = 1 << p; 505 | 506 | let now = Instant::now(); 507 | let pp = crate::multi::PublicParameters::::setup(&max_degree, &N, &m, &p); 508 | println!("time to setup {:?}", now.elapsed()); 509 | 510 | let mut positions: Vec = vec![]; 511 | for i in 0..m { 512 | // generate positions evenly distributed in the set 513 | let i_j: usize = i * (max_degree / m); 514 | positions.push(i_j); 515 | } 516 | 517 | let tau_polys = generate_lagrange_polynomials_subset(&positions, &pp); 518 | for j in 0..m { 519 | for k in 0..m { 520 | if k == j { 521 | assert_eq!( 522 | tau_polys[j].evaluate(&pp.domain_N.element(positions[k])), 523 | E::Fr::one() 524 | ) 525 | } else { 526 | assert_eq!( 527 | tau_polys[j].evaluate(&pp.domain_N.element(positions[k])), 528 | E::Fr::zero() 529 | ) 530 | } 531 | } 532 | } 533 | } 534 | 535 | #[test] 536 | #[allow(non_snake_case)] 537 | pub fn test_Q_g2() { 538 | test_Q_g2_helper::(); 539 | test_Q_g2_helper::(); 540 | } 541 | 542 | #[allow(non_snake_case)] 543 | pub fn test_Q_g2_helper() { 544 | let rng = &mut ark_std::test_rng(); 545 | 546 | // current kzg setup should be changed with output from a setup ceremony 547 | let p: usize = 6; // bitlength of poly degree 548 | let max_degree: usize = (1 << p) + 2; 549 | let actual_degree: usize = (1 << p) - 1; 550 | let m: usize = 1 << (p / 2); 551 | let N: usize = 1 << p; 552 | let pp = crate::multi::PublicParameters::setup(&max_degree, &N, &m, &p); 553 | 554 | // Setting up test instance to run evaluate on. 555 | // test randomness for c_poly is same everytime. 556 | // test index equals 5 everytime 557 | // g_c = g^(c(x)) 558 | 559 | let c_poly = DensePolynomial::::rand(actual_degree, rng); 560 | let c_com = KZGCommit::::commit_g1(&pp.poly_ck, &c_poly); 561 | 562 | let now = Instant::now(); 563 | let openings = KZGCommit::::multiple_open::(&c_poly, &pp.g2_powers, p); 564 | println!("Multi advanced computed in {:?}", now.elapsed()); 565 | 566 | let mut positions: Vec = vec![]; 567 | for i in 0..m { 568 | let i_j: usize = i * (max_degree / m); 569 | positions.push(i_j); 570 | } 571 | 572 | let now = Instant::now(); 573 | 574 | // Compute proof 575 | let Q: E::G2Affine = 576 | KZGCommit::::aggregate_proof_g2(&openings, &positions, &pp.domain_N); 577 | println!( 578 | "Full proof for {:?} positions computed in {:?}", 579 | m, 580 | now.elapsed() 581 | ); 582 | 583 | // Compute commitment to C_I 584 | let mut C_I = DensePolynomial::from_coefficients_slice(&[E::Fr::zero()]); // C_I = sum_j c_j*tau_j 585 | let tau_polys = generate_lagrange_polynomials_subset(&positions, &pp); 586 | for j in 0..m { 587 | C_I = &C_I + &(&tau_polys[j] * c_poly.evaluate(&pp.domain_N.element(positions[j]))); 588 | // sum_j c_j*tau_j 589 | } 590 | let c_I_com = KZGCommit::::commit_g1(&pp.poly_ck, &C_I); 591 | 592 | // Compute commitment to z_I 593 | let mut z_I = DensePolynomial::from_coefficients_slice(&[E::Fr::one()]); 594 | for j in 0..m { 595 | z_I = &z_I 596 | * &DensePolynomial::from_coefficients_slice(&[ 597 | -pp.domain_N.element(positions[j]), 598 | E::Fr::one(), 599 | ]); 600 | } 601 | let z_I_com = KZGCommit::::commit_g1(&pp.poly_ck, &z_I); 602 | 603 | // pairing check 604 | let pairing1 = E::pairing( 605 | (c_com.into_projective() - c_I_com.into_projective()).into_affine(), 606 | pp.g2_powers[0], 607 | ); 608 | let pairing2 = E::pairing(z_I_com, Q); 609 | assert_eq!(pairing1, pairing2); 610 | } 611 | 612 | #[test] 613 | fn test_single() { 614 | test_single_helper::(); 615 | test_single_helper::(); 616 | } 617 | 618 | fn test_single_helper() { 619 | let mut rng = test_rng(); 620 | 621 | // setting public parameters 622 | // current kzg setup should be changed with output from a setup ceremony 623 | let max_degree: usize = 100; 624 | let actual_degree: usize = 63; 625 | let pp = caulk_single_setup(max_degree, actual_degree, &mut rng); 626 | 627 | // Setting up test instance to run evaluate on. 628 | // test randomness for c_poly is same everytime. 629 | // test index equals 5 everytime 630 | // g_c = g^(c(x)) 631 | let rng = &mut test_rng(); 632 | let c_poly = DensePolynomial::::rand(actual_degree, rng); 633 | let (_c_com, c_com_open) = KZG10::::commit(&pp.poly_ck, &c_poly, None, None).unwrap(); 634 | 635 | let i: usize = 6; 636 | let q = single_open_default(&c_poly, &c_com_open, &pp.poly_ck, i, actual_degree); 637 | let q2 = single_open_fast(&c_poly, &pp.poly_ck, i, actual_degree); 638 | assert_eq!(q, q2); 639 | } 640 | 641 | #[test] 642 | pub fn test_multi() { 643 | test_multi_helper::(); 644 | test_multi_helper::(); 645 | } 646 | 647 | pub fn test_multi_helper() { 648 | let mut rng = test_rng(); 649 | 650 | // current kzg setup should be changed with output from a setup ceremony 651 | let p: usize = 9; 652 | let max_degree: usize = 1 << p + 1; 653 | let actual_degree: usize = (1 << p) - 1; 654 | let pp = caulk_single_setup(max_degree, actual_degree, &mut rng); 655 | 656 | // Setting up test instance to run evaluate on. 657 | // test randomness for c_poly is same everytime. 658 | // test index equals 5 everytime 659 | // g_c = g^(c(x)) 660 | let c_poly = DensePolynomial::::rand(actual_degree, &mut rng); 661 | let (c_com, c_com_open) = KZG10::::commit(&pp.poly_ck, &c_poly, None, None).unwrap(); 662 | let _g_c = c_com.0; 663 | 664 | let now = Instant::now(); 665 | let q = multiple_open_naive(&c_poly, &c_com_open, &pp.poly_ck, actual_degree); 666 | println!("Multi naive computed in {:?}", now.elapsed()); 667 | 668 | let now = Instant::now(); 669 | let q2 = KZGCommit::::multiple_open::(&c_poly, &pp.poly_ck.powers_of_g, p); 670 | println!("Multi advanced computed in {:?}", now.elapsed()); 671 | assert_eq!(q, q2); 672 | } 673 | 674 | #[test] 675 | fn test_commit() { 676 | test_commit_helper::(); 677 | test_commit_helper::(); 678 | } 679 | 680 | pub fn test_commit_helper() { 681 | let mut rng = test_rng(); 682 | 683 | // current kzg setup should be changed with output from a setup ceremony 684 | let max_degree: usize = 100; 685 | let actual_degree: usize = 63; 686 | let pp = caulk_single_setup(max_degree, actual_degree, &mut rng); 687 | 688 | // Setting up test instance to run evaluate on. 689 | // test randomness for c_poly is same everytime. 690 | // g_c = g^(c(x)) 691 | let c_poly = DensePolynomial::::rand(actual_degree, &mut rng); 692 | let (c_com, _c_com_open) = KZG10::::commit(&pp.poly_ck, &c_poly, None, None).unwrap(); 693 | let g_c1 = c_com.0; 694 | 695 | let g_c2 = commit_direct(&c_poly, &pp.poly_ck); 696 | assert_eq!(g_c1, g_c2); 697 | println!("commit test passed") 698 | } 699 | 700 | /// Various functions that are used for testing 701 | 702 | fn commit_direct( 703 | c_poly: &DensePolynomial, // c(X) 704 | poly_ck: &Powers, // SRS 705 | ) -> E::G1Affine { 706 | assert!(c_poly.coeffs.len() <= poly_ck.powers_of_g.len()); 707 | let mut com = poly_ck.powers_of_g[0].mul(c_poly.coeffs[0]); 708 | for i in 1..c_poly.coeffs.len() { 709 | com = com + poly_ck.powers_of_g[i].mul(c_poly.coeffs[i]); 710 | } 711 | com.into_affine() 712 | } 713 | 714 | // compute all openings to c_poly by mere calling `open` N times 715 | fn multiple_open_naive( 716 | c_poly: &DensePolynomial, 717 | c_com_open: &Randomness>, 718 | poly_ck: &Powers, 719 | degree: usize, 720 | ) -> Vec { 721 | let input_domain: GeneralEvaluationDomain = EvaluationDomain::new(degree).unwrap(); 722 | let mut res: Vec = vec![]; 723 | for i in 0..input_domain.size() { 724 | let omega_i = input_domain.element(i); 725 | res.push(kzg_open_g1_test::(&c_poly, &omega_i, &c_com_open, &poly_ck).w); 726 | } 727 | res 728 | } 729 | 730 | //////////////////////////////////////////////// 731 | fn kzg_open_g1_test( 732 | p: &DensePolynomial, 733 | omega_5: &E::Fr, 734 | polycom_open: &Randomness>, 735 | poly_ck: &Powers, 736 | ) -> Proof { 737 | let rng = &mut ark_std::test_rng(); 738 | 739 | let (witness_polynomial, _random_witness_polynomial) = 740 | KZG10::::compute_witness_polynomial(p, omega_5.clone(), polycom_open).unwrap(); 741 | 742 | let (temp0, _temp1) = KZG10::commit(poly_ck, &witness_polynomial, None, Some(rng)).unwrap(); 743 | Proof { 744 | w: temp0.0, 745 | random_v: None, 746 | } 747 | } 748 | 749 | // compute KZG proof Q = g1_q = g^( (c(x) - c(w^i)) / (x - w^i) ) where x is 750 | // secret, w^i is the point where we open, and c(X) is the committed polynomial 751 | fn single_open_default( 752 | c_poly: &DensePolynomial, // c(X) 753 | c_com_open: &Randomness>, // 754 | poly_ck: &Powers, 755 | i: usize, // 756 | degree: usize, 757 | ) -> E::G1Affine { 758 | let input_domain: GeneralEvaluationDomain = EvaluationDomain::new(degree).unwrap(); 759 | let omega_i = input_domain.element(i); 760 | let c_poly_open = kzg_open_g1_test(&c_poly, &omega_i, &c_com_open, &poly_ck); 761 | c_poly_open.w 762 | } 763 | 764 | // KZG proof/opening at point y for c(X) = sum_i c_i X^i 765 | //(1)T_y(X) = sum_i t_i X^i 766 | //(2) t_{deg-1} = c_deg 767 | //(3) t_j = c_{j+1} + y*t_{j+1} 768 | fn single_open_fast( 769 | c_poly: &DensePolynomial, // c(X) 770 | poly_ck: &Powers, // SRS 771 | i: usize, // y=w^i 772 | degree: usize, // degree of c(X) 773 | ) -> E::G1Affine { 774 | // computing opening point 775 | let input_domain: GeneralEvaluationDomain = EvaluationDomain::new(degree).unwrap(); 776 | let y = input_domain.element(i); 777 | 778 | // compute quotient 779 | let mut t_poly = c_poly.clone(); 780 | t_poly.coeffs.remove(0); // shifting indices 781 | for j in (0..t_poly.len() - 1).rev() { 782 | t_poly.coeffs[j] = c_poly.coeffs[j + 1] + y * t_poly.coeffs[j + 1] 783 | } 784 | 785 | // commit 786 | let (t_com, _) = KZG10::commit(&poly_ck, &t_poly, None, None).unwrap(); 787 | t_com.0 788 | } 789 | } 790 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | mod dft; 2 | mod kzg; 3 | pub mod multi; 4 | mod pedersen; 5 | mod single; 6 | mod transcript; 7 | pub(crate) mod util; 8 | 9 | pub use dft::*; 10 | pub use kzg::KZGCommit; 11 | pub use multi::{ 12 | compute_lookup_proof, prove_multiunity, 13 | setup::{LookupParameters, VerifierPublicParameters}, 14 | verify_lookup_proof, verify_multiunity, verify_multiunity_defer_pairing, PublicParameters, 15 | }; 16 | pub use pedersen::PedersenParam; 17 | pub use single::{caulk_single_prove, caulk_single_verify, setup::caulk_single_setup}; 18 | pub use transcript::CaulkTranscript; 19 | -------------------------------------------------------------------------------- /src/multi/mod.rs: -------------------------------------------------------------------------------- 1 | // This file includes the Caulk prover and verifier for single openings. 2 | // The protocol is described in Figure 3. 3 | 4 | pub mod setup; 5 | mod unity; 6 | 7 | use crate::{kzg::generate_lagrange_polynomials_subset, CaulkTranscript, KZGCommit}; 8 | use ark_ec::{AffineCurve, PairingEngine, ProjectiveCurve}; 9 | use ark_ff::{Field, PrimeField}; 10 | use ark_poly::{ 11 | univariate::DensePolynomial, EvaluationDomain, Evaluations as EvaluationsOnDomain, Evaluations, 12 | GeneralEvaluationDomain, Polynomial, UVPolynomial, 13 | }; 14 | use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; 15 | use ark_std::{cfg_into_iter, end_timer, rand::RngCore, start_timer, One, UniformRand, Zero}; 16 | #[cfg(feature = "parallel")] 17 | use rayon::iter::{IntoParallelIterator, ParallelIterator}; 18 | pub use setup::PublicParameters; 19 | use std::{ 20 | convert::TryInto, 21 | fs::File, 22 | io::{Read, Write}, 23 | ops::MulAssign, 24 | time::Instant, 25 | vec::Vec, 26 | }; 27 | pub use unity::{ 28 | prove_multiunity, verify_multiunity, verify_multiunity_defer_pairing, ProofMultiUnity, 29 | }; 30 | 31 | pub struct LookupInstance { 32 | pub c_com: C, // polynomial C(X) that represents a table 33 | pub phi_com: C, // polynomial phi(X) that represents the values to look up 34 | } 35 | 36 | pub struct LookupProverInput { 37 | pub c_poly: DensePolynomial, // polynomial C(X) that represents a table 38 | pub phi_poly: DensePolynomial, // polynomial phi(X) that represents the values to look up 39 | pub positions: Vec, // 40 | pub openings: Vec, 41 | } 42 | 43 | #[derive(Debug, PartialEq)] 44 | // Data structure to be stored in a file: polynomial, its commitment, and its 45 | // openings (for certain SRS) 46 | pub struct TableInput { 47 | pub c_poly: DensePolynomial, 48 | pub c_com: E::G1Affine, 49 | pub openings: Vec, 50 | } 51 | 52 | // Lookup proof data structure 53 | #[allow(non_snake_case)] 54 | pub struct LookupProof { 55 | pub C_I_com: E::G1Affine, // Commitment to C_I(X) 56 | pub H1_com: E::G2Affine, // Commitment to H_1(X) 57 | pub H2_com: E::G1Affine, // Commitment to H_2(X) 58 | pub u_com: E::G1Affine, // Commitment to u(X) 59 | pub z_I_com: E::G1Affine, // Commitment to z_I(X) 60 | pub v1: E::Fr, 61 | pub v2: E::Fr, 62 | pub pi1: E::G1Affine, 63 | pub pi2: E::G1Affine, 64 | pub pi3: E::G1Affine, 65 | } 66 | 67 | impl TableInput { 68 | fn store(&self, path: &str) { 69 | // 1. Polynomial 70 | let mut o_bytes = vec![]; 71 | let mut f = File::create(path).expect("Unable to create file"); 72 | let len: u32 = self.c_poly.len().try_into().unwrap(); 73 | let len_bytes = len.to_be_bytes(); 74 | f.write_all(&len_bytes).expect("Unable to write data"); 75 | let len32: usize = len.try_into().unwrap(); 76 | for i in 0..len32 { 77 | self.c_poly.coeffs[i] 78 | .serialize_uncompressed(&mut o_bytes) 79 | .ok(); 80 | } 81 | f.write_all(&o_bytes).expect("Unable to write data"); 82 | 83 | // 2. Commitment 84 | o_bytes.clear(); 85 | self.c_com.serialize_uncompressed(&mut o_bytes).ok(); 86 | f.write_all(&o_bytes).expect("Unable to write data"); 87 | 88 | // 3. Openings 89 | o_bytes.clear(); 90 | let len: u32 = self.openings.len().try_into().unwrap(); 91 | let len_bytes = len.to_be_bytes(); 92 | f.write_all(&len_bytes).expect("Unable to write data"); 93 | let len32: usize = len.try_into().unwrap(); 94 | for i in 0..len32 { 95 | self.openings[i].serialize_uncompressed(&mut o_bytes).ok(); 96 | } 97 | f.write_all(&o_bytes).expect("Unable to write data"); 98 | } 99 | 100 | fn load(path: &str) -> TableInput { 101 | const FR_UNCOMPR_SIZE: usize = 32; 102 | const G1_UNCOMPR_SIZE: usize = 96; 103 | const G2_UNCOMPR_SIZE: usize = 192; 104 | let mut data = Vec::new(); 105 | let mut f = File::open(path).expect("Unable to open file"); 106 | f.read_to_end(&mut data).expect("Unable to read data"); 107 | 108 | // 1. reading c_poly 109 | let mut cur_counter: usize = 0; 110 | let len_bytes: [u8; 4] = (&data[0..4]).try_into().unwrap(); 111 | let len: u32 = u32::from_be_bytes(len_bytes); 112 | let mut coeffs = vec![]; 113 | let len32: usize = len.try_into().unwrap(); 114 | cur_counter += 4; 115 | for i in 0..len32 { 116 | let buf_bytes = 117 | &data[cur_counter + i * FR_UNCOMPR_SIZE..cur_counter + (i + 1) * FR_UNCOMPR_SIZE]; 118 | let tmp = E::Fr::deserialize_unchecked(buf_bytes).unwrap(); 119 | coeffs.push(tmp); 120 | } 121 | cur_counter += len32 * FR_UNCOMPR_SIZE; 122 | 123 | // 2. c_com 124 | let buf_bytes = &data[cur_counter..cur_counter + G1_UNCOMPR_SIZE]; 125 | let c_com = E::G1Affine::deserialize_unchecked(buf_bytes).unwrap(); 126 | cur_counter += G1_UNCOMPR_SIZE; 127 | 128 | // 3 openings 129 | let len_bytes: [u8; 4] = (&data[cur_counter..cur_counter + 4]).try_into().unwrap(); 130 | let len: u32 = u32::from_be_bytes(len_bytes); 131 | let mut openings = vec![]; 132 | let len32: usize = len.try_into().unwrap(); 133 | cur_counter += 4; 134 | for _ in 0..len32 { 135 | let buf_bytes = &data[cur_counter..cur_counter + G2_UNCOMPR_SIZE]; 136 | let tmp = E::G2Affine::deserialize_unchecked(buf_bytes).unwrap(); 137 | openings.push(tmp); 138 | cur_counter += G2_UNCOMPR_SIZE; 139 | } 140 | 141 | TableInput { 142 | c_poly: DensePolynomial { coeffs }, 143 | c_com, 144 | openings, 145 | } 146 | } 147 | } 148 | 149 | #[allow(non_snake_case)] 150 | pub fn compute_lookup_proof( 151 | instance: &LookupInstance, 152 | input: &LookupProverInput, 153 | srs: &PublicParameters, 154 | rng: &mut R, 155 | ) -> (LookupProof, ProofMultiUnity) { 156 | let timer = start_timer!(|| "lookup proof generation"); 157 | 158 | let m = input.positions.len(); 159 | 160 | /////////////////////////////////////////////////////////////////// 161 | // 1. Blinders 162 | /////////////////////////////////////////////////////////////////// 163 | let step_1_timer = start_timer!(|| "step 1"); 164 | // provers blinders for zero-knowledge 165 | let r1 = E::Fr::rand(rng); 166 | let r2 = E::Fr::rand(rng); 167 | let r3 = E::Fr::rand(rng); 168 | let r4 = E::Fr::rand(rng); 169 | let r5 = E::Fr::rand(rng); 170 | let r6 = E::Fr::rand(rng); 171 | let r7 = E::Fr::rand(rng); 172 | end_timer!(step_1_timer); 173 | /////////////////////////////////////////////////////////////////// 174 | // 2. Compute z_I(X) = r1 prod_{i in I} (X - w^i) 175 | /////////////////////////////////////////////////////////////////// 176 | let step_2_timer = start_timer!(|| "step 2"); 177 | // z_I includes each position only once. 178 | let mut positions_no_repeats = Vec::new(); 179 | for i in 0..input.positions.len() { 180 | if positions_no_repeats.contains(&input.positions[i]) { 181 | } else { 182 | positions_no_repeats.push(input.positions[i]); 183 | } 184 | } 185 | 186 | // insert 0 into z_I so that we can pad when m is not a power of 2. 187 | if positions_no_repeats.contains(&0usize) { 188 | } else { 189 | positions_no_repeats.push(0usize); 190 | } 191 | 192 | // z_I(X) 193 | let mut z_I = DensePolynomial::from_coefficients_slice(&[r1]); 194 | for &pos in positions_no_repeats.iter() { 195 | z_I = &z_I 196 | * &DensePolynomial::from_coefficients_slice(&[ 197 | -srs.domain_N.element(pos), 198 | E::Fr::one(), 199 | ]); 200 | } 201 | end_timer!(step_2_timer); 202 | /////////////////////////////////////////////////////////////////// 203 | // 3. Compute C_I(X) = (r_2+r_3X + r4X^2)*Z_I(X) + sum_j c_j*tau_j(X) 204 | /////////////////////////////////////////////////////////////////// 205 | let step_3_timer = start_timer!(|| "step 3"); 206 | let mut c_I_poly = DensePolynomial::from_coefficients_slice(&[E::Fr::zero()]); 207 | 208 | // tau_polys[i] = 1 at positions_no_repeats[i] and 0 at positions_no_repeats[j] 209 | // Takes m^2 time, or 36ms when m = 32. Can be done in m log^2(m) time if this 210 | // ever becomes a bottleneck. See https://people.csail.mit.edu/devadas/pubs/scalable_thresh.pdf 211 | let tau_polys = generate_lagrange_polynomials_subset(&positions_no_repeats, srs); 212 | 213 | // C_I(X) = sum_j c_j*tau_j(X) 214 | // Takes m^2 time, or 38ms when m = 32. Can be done faster if we store c_poly 215 | // evaluations. 216 | for j in 0..positions_no_repeats.len() { 217 | c_I_poly = &c_I_poly 218 | + &(&tau_polys[j] 219 | * input 220 | .c_poly 221 | .evaluate(&srs.domain_N.element(positions_no_repeats[j]))); // sum_j c_j*tau_j 222 | } 223 | 224 | // extra_blinder = r2 + r3 X + r4 X^2 225 | let extra_blinder = DensePolynomial::from_coefficients_slice(&[r2, r3, r4]); 226 | 227 | // C_I(X) = C_I(X) + z_I(X) * (r2 + r3 X + r4 X^2) 228 | c_I_poly = &c_I_poly + &(&z_I * &extra_blinder); 229 | 230 | end_timer!(step_3_timer); 231 | /////////////////////////////////////////////////////////////////// 232 | // 4. Compute H1 233 | /////////////////////////////////////////////////////////////////// 234 | let step_4_timer = start_timer!(|| "step 4"); 235 | // Compute [Q(x)]_2 by aggregating kzg proofs such that 236 | // Q(X) = ( C(X) - sum_{i in I} c_{i+1} tau_i(X) ) / ( prod_{i in I} (X - 237 | // w^i) ) 238 | let g2_Q = 239 | KZGCommit::::aggregate_proof_g2(&input.openings, &positions_no_repeats, &srs.domain_N); 240 | 241 | // blind_com = [ r2 + r3 x + r4 x^2 ]_2 242 | let blind_com = KZGCommit::::commit_g2(&srs.g2_powers, &extra_blinder); 243 | 244 | // H1_com = [ r1^{-1} Q(x) ]_2 - blind_com 245 | let H1_com = (g2_Q.mul(r1.inverse().unwrap()) - blind_com.into_projective()).into_affine(); 246 | 247 | end_timer!(step_4_timer); 248 | /////////////////////////////////////////////////////////////////// 249 | // 5. Compute u(X) = sum_j w^{i_j} mu_j(X) + (r5 + r6 X + r7 X^2) z_{Vm}(X) 250 | /////////////////////////////////////////////////////////////////// 251 | let step_5_timer = start_timer!(|| "step 5"); 252 | // u(X) = sum_j w^{i_j} mu_j(X) 253 | let mut u_vals = vec![]; 254 | for j in 0..m { 255 | u_vals.push(srs.domain_N.element(input.positions[j])); 256 | } 257 | 258 | // u(X) = u(X) + (r5 + r6 X + r7 X^2) z_{Vm}(X) 259 | let extra_blinder2 = DensePolynomial::from_coefficients_slice(&[r5, r6, r7]); 260 | let u_poly = &EvaluationsOnDomain::from_vec_and_domain(u_vals.clone(), srs.domain_m) 261 | .interpolate() 262 | + &(extra_blinder2.mul_by_vanishing_poly(srs.domain_m)); 263 | 264 | end_timer!(step_5_timer); 265 | /////////////////////////////////////////////////////////////////// 266 | // 6. Commitments 267 | /////////////////////////////////////////////////////////////////// 268 | let step_6_timer = start_timer!(|| "step 6"); 269 | let z_I_com = KZGCommit::::commit_g1(&srs.poly_ck, &z_I); 270 | let C_I_com = KZGCommit::::commit_g1(&srs.poly_ck, &c_I_poly); 271 | let u_com = KZGCommit::::commit_g1(&srs.poly_ck, &u_poly); 272 | 273 | end_timer!(step_6_timer); 274 | /////////////////////////////////////////////////////////////////// 275 | // 7 Prepare unity proof 276 | /////////////////////////////////////////////////////////////////// 277 | let step_7_timer = start_timer!(|| "step 7"); 278 | // transcript initialised to zero 279 | let mut transcript = CaulkTranscript::new(); 280 | 281 | // let now = Instant::now(); 282 | let unity_proof = prove_multiunity(srs, &mut transcript, &u_com, &u_vals, extra_blinder2); 283 | // println!("Time to prove unity {:?}", now.elapsed()); 284 | 285 | // quick test can be uncommented to check if unity proof verifies 286 | // let unity_check = verify_multiunity( &srs, &mut Fr::zero(), u_com.0.clone(), 287 | // &unity_proof ); println!("unity_check = {}", unity_check); 288 | 289 | end_timer!(step_7_timer); 290 | /////////////////////////////////////////////////////////////////// 291 | // 8. Hash outputs to get chi 292 | /////////////////////////////////////////////////////////////////// 293 | let step_8_timer = start_timer!(|| "step 8"); 294 | transcript.append_element(b"c_com", &instance.c_com); 295 | transcript.append_element(b"phi_com", &instance.phi_com); 296 | transcript.append_element(b"u_bar_alpha", &unity_proof.g1_u_bar_alpha); 297 | transcript.append_element(b"h2_alpha", &unity_proof.g1_h_2_alpha); 298 | transcript.append_element(b"pi_1", &unity_proof.pi_1); 299 | transcript.append_element(b"pi_2", &unity_proof.pi_2); 300 | transcript.append_element(b"pi_3", &unity_proof.pi_3); 301 | transcript.append_element(b"pi_4", &unity_proof.pi_4); 302 | transcript.append_element(b"pi_5", &unity_proof.pi_5); 303 | transcript.append_element(b"C_I_com", &C_I_com); 304 | transcript.append_element(b"z_I_com", &z_I_com); 305 | transcript.append_element(b"u_com", &u_com); 306 | 307 | transcript.append_element(b"h1_com", &H1_com); 308 | 309 | transcript.append_element(b"v1", &unity_proof.v1); 310 | transcript.append_element(b"v2", &unity_proof.v2); 311 | transcript.append_element(b"v3", &unity_proof.v3); 312 | 313 | let chi = transcript.get_and_append_challenge(b"chi"); 314 | 315 | end_timer!(step_8_timer); 316 | /////////////////////////////////////////////////////////////////// 317 | // 9. Compute z_I( u(X) ) 318 | /////////////////////////////////////////////////////////////////// 319 | let step_9_timer = start_timer!(|| "step 9"); 320 | // Need a bigger domain to compute z_I(u(X)) over. 321 | // Has size O(m^2) 322 | let domain_m_sq: GeneralEvaluationDomain = 323 | GeneralEvaluationDomain::new(z_I.len() * u_poly.len() + 2).unwrap(); 324 | 325 | // id_poly(X) = 0 for sigma_i < m and 1 for sigma_i > m 326 | // used for when m is not a power of 2 327 | let mut id_poly = DensePolynomial::from_coefficients_slice(&[E::Fr::one()]); 328 | id_poly = &id_poly - &srs.id_poly; 329 | 330 | // Compute z_I( u(X) + w^0 id(X) ) 331 | // Computing z_I(u(X)) is done naively and could be faster. Currently this is 332 | // not a bottleneck 333 | let evals: Vec = cfg_into_iter!(0..domain_m_sq.size()) 334 | .map(|k| { 335 | z_I.evaluate( 336 | &(u_poly.evaluate(&domain_m_sq.element(k)) 337 | + id_poly.evaluate(&domain_m_sq.element(k))), 338 | ) 339 | }) 340 | .collect(); 341 | let z_I_u_poly = Evaluations::from_vec_and_domain(evals, domain_m_sq).interpolate(); 342 | 343 | end_timer!(step_9_timer); 344 | /////////////////////////////////////////////////////////////////// 345 | // 10. Compute C_I(u(X))-phi(X) 346 | /////////////////////////////////////////////////////////////////// 347 | let step_10_timer = start_timer!(|| "step 10"); 348 | // Compute C_I( u(X) ) 349 | // Computing C_I(u(X)) is done naively and could be faster. Currently this is 350 | // not a bottleneck 351 | 352 | // Actually compute c_I( u(X) + id(X) ) in case m is not a power of 2 353 | let evals: Vec = cfg_into_iter!(0..domain_m_sq.size()) 354 | .map(|k| { 355 | c_I_poly.evaluate( 356 | &(u_poly.evaluate(&domain_m_sq.element(k)) 357 | + id_poly.evaluate(&domain_m_sq.element(k))), 358 | ) 359 | }) 360 | .collect(); 361 | 362 | // c_I_u_poly = C_I( u(X) ) - phi(X) 363 | let c_I_u_poly = 364 | &Evaluations::from_vec_and_domain(evals, domain_m_sq).interpolate() - &input.phi_poly; 365 | 366 | end_timer!(step_10_timer); 367 | /////////////////////////////////////////////////////////////////// 368 | // 11. Compute H2 369 | /////////////////////////////////////////////////////////////////// 370 | let step_11_timer = start_timer!(|| "step 11"); 371 | // temp_poly(X) = z_I(u(X)) + chi [ C_I(u(X)) - phi(X) ] 372 | let temp_poly = &z_I_u_poly + &(&c_I_u_poly * chi); 373 | 374 | // H2(X) = temp_poly / z_Vm(X) 375 | let (H2_poly, rem) = temp_poly.divide_by_vanishing_poly(srs.domain_m).unwrap(); 376 | assert!( 377 | rem == DensePolynomial::from_coefficients_slice(&[E::Fr::zero()]), 378 | "H_2(X) doesn't divide" 379 | ); 380 | 381 | end_timer!(step_11_timer); 382 | /////////////////////////////////////////////////////////////////// 383 | // 12. Compute commitments to H2 384 | /////////////////////////////////////////////////////////////////// 385 | let step_12_timer = start_timer!(|| "step 12"); 386 | let H2_com = KZGCommit::::commit_g1(&srs.poly_ck, &H2_poly); 387 | // println!("Time to commit to H2 {:?}", now.elapsed()); 388 | 389 | end_timer!(step_12_timer); 390 | /////////////////////////////////////////////////////////////////// 391 | // 13. Hash outputs to get alpha 392 | /////////////////////////////////////////////////////////////////// 393 | let step_13_timer = start_timer!(|| "step 13"); 394 | transcript.append_element(b"h2", &H2_com); 395 | let alpha = transcript.get_and_append_challenge(b"alpha"); 396 | 397 | // last hash so don't need to update hash_input 398 | // hash_input = alpha.clone(); 399 | 400 | end_timer!(step_13_timer); 401 | /////////////////////////////////////////////////////////////////// 402 | // 14. Open u at alpha, get v1 403 | /////////////////////////////////////////////////////////////////// 404 | let step_14_timer = start_timer!(|| "step 14"); 405 | let (evals1, pi1) = KZGCommit::::open_g1_batch(&srs.poly_ck, &u_poly, None, &[alpha]); 406 | let v1 = evals1[0]; 407 | 408 | end_timer!(step_14_timer); 409 | /////////////////////////////////////////////////////////////////// 410 | // 15. Compute p1(X) and open at v1 411 | /////////////////////////////////////////////////////////////////// 412 | let step_15_timer = start_timer!(|| "step 15"); 413 | // v1_id = u(alpha) + id(alpha) for when m is not a power of 2 414 | let v1_id = v1 + id_poly.evaluate(&alpha); 415 | 416 | // p1(X) = z_IX() + chi cI(X) 417 | let p1_poly = &z_I + &(&c_I_poly * chi); 418 | 419 | let (evals2, pi2) = KZGCommit::::open_g1_batch(&srs.poly_ck, &p1_poly, None, &[v1_id]); 420 | 421 | end_timer!(step_15_timer); 422 | /////////////////////////////////////////////////////////////////// 423 | // 16. Compute p2(X) and open p2 at alpha 424 | /////////////////////////////////////////////////////////////////// 425 | let step_16_timer = start_timer!(|| "step 16"); 426 | // p2(X) = zI(u(alpha)) + chi C_I( u(alpha) ) 427 | let mut p2_poly = DensePolynomial::from_coefficients_slice(&[ 428 | z_I.evaluate(&v1_id) + chi * c_I_poly.evaluate(&v1_id) 429 | ]); 430 | 431 | // p2(X) = p2(X) - chi phi(X) 432 | p2_poly = &p2_poly - &(&input.phi_poly * chi); 433 | 434 | // p2(X) = p2(X) - zVm(alpha) H2(X) 435 | let zVm: DensePolynomial = srs.domain_m.vanishing_polynomial().into(); 436 | 437 | p2_poly = &p2_poly - &(&H2_poly * zVm.evaluate(&alpha)); 438 | 439 | // Open p2(X) at alpha 440 | let (evals3, pi3) = KZGCommit::::open_g1_batch(&srs.poly_ck, &p2_poly, None, &[alpha]); 441 | 442 | // check that p2_poly(alpha) = 0 443 | assert!(evals3[0] == E::Fr::zero(), "p2(alpha) does not equal 0"); 444 | 445 | end_timer!(step_16_timer); 446 | /////////////////////////////////////////////////////////////////// 447 | // 17. Compose proof 448 | /////////////////////////////////////////////////////////////////// 449 | let proof = LookupProof { 450 | C_I_com, 451 | H1_com, 452 | H2_com, 453 | z_I_com, 454 | u_com, 455 | v1, 456 | v2: evals2[0], 457 | pi1, 458 | pi2, 459 | pi3, 460 | }; 461 | end_timer!(timer); 462 | (proof, unity_proof) 463 | } 464 | 465 | #[allow(non_snake_case)] 466 | pub fn verify_lookup_proof( 467 | c_com: &E::G1Affine, 468 | phi_com: &E::G1Affine, 469 | proof: &LookupProof, 470 | unity_proof: &ProofMultiUnity, 471 | srs: &PublicParameters, 472 | rng: &mut R, 473 | ) -> bool { 474 | let timer = start_timer!(|| "lookup proof verification"); 475 | /////////////////////////////////////////////////////////////////// 476 | // 1. check unity 477 | /////////////////////////////////////////////////////////////////// 478 | 479 | // hash_input initialised to zero 480 | let mut transcript = CaulkTranscript::new(); 481 | 482 | let unity_check = 483 | verify_multiunity_defer_pairing(srs, &mut transcript, &proof.u_com, unity_proof); 484 | 485 | /////////////////////////////////////////////////////////////////// 486 | // 2. Hash outputs to get chi 487 | /////////////////////////////////////////////////////////////////// 488 | 489 | transcript.append_element(b"c_com", c_com); 490 | transcript.append_element(b"phi_com", phi_com); 491 | transcript.append_element(b"u_bar_alpha", &unity_proof.g1_u_bar_alpha); 492 | transcript.append_element(b"h2_alpha", &unity_proof.g1_h_2_alpha); 493 | transcript.append_element(b"pi_1", &unity_proof.pi_1); 494 | transcript.append_element(b"pi_2", &unity_proof.pi_2); 495 | transcript.append_element(b"pi_3", &unity_proof.pi_3); 496 | transcript.append_element(b"pi_4", &unity_proof.pi_4); 497 | transcript.append_element(b"pi_5", &unity_proof.pi_5); 498 | transcript.append_element(b"C_I_com", &proof.C_I_com); 499 | transcript.append_element(b"z_I_com", &proof.z_I_com); 500 | transcript.append_element(b"u_com", &proof.u_com); 501 | 502 | transcript.append_element(b"h1_com", &proof.H1_com); 503 | 504 | transcript.append_element(b"v1", &unity_proof.v1); 505 | transcript.append_element(b"v2", &unity_proof.v2); 506 | transcript.append_element(b"v3", &unity_proof.v3); 507 | 508 | let chi = transcript.get_and_append_challenge(b"chi"); 509 | 510 | /////////////////////////////////////////////////////////////////// 511 | // 3. Hash outputs to get alpha 512 | /////////////////////////////////////////////////////////////////// 513 | transcript.append_element(b"h2", &proof.H2_com); 514 | let alpha = transcript.get_and_append_challenge(b"alpha"); 515 | 516 | // last hash so don't need to update hash_input 517 | // hash_input = alpha.clone(); 518 | 519 | /////////////////////////////////////////////////////////////////// 520 | // 4. Check pi_1 521 | /////////////////////////////////////////////////////////////////// 522 | 523 | // KZG.Verify(srs_KZG, [u]_1, deg = bot, alpha, v1, pi1) 524 | let check1 = KZGCommit::::verify_g1_defer_pairing( 525 | &srs.poly_ck.powers_of_g, 526 | &srs.g2_powers, 527 | &proof.u_com, 528 | None, 529 | &[alpha], 530 | &[proof.v1], 531 | &proof.pi1, 532 | ); 533 | 534 | /////////////////////////////////////////////////////////////////// 535 | // 5. Check pi_2 536 | /////////////////////////////////////////////////////////////////// 537 | 538 | // v1_id = u(alpha)+ id(alpha) for when m is not a power of 2 539 | let v1_id = proof.v1 + (E::Fr::one() - srs.id_poly.evaluate(&alpha)); 540 | 541 | // [P1]_1 = [z_I]_1 + chi [c_I]_1 542 | let p1_com = (proof.z_I_com.into_projective() + proof.C_I_com.mul(chi)).into_affine(); 543 | 544 | // KZG.Verify(srs_KZG, [P1]_1, deg = bot, v1_id, v2, pi2) 545 | let check2 = KZGCommit::::verify_g1_defer_pairing( 546 | &srs.poly_ck.powers_of_g, 547 | &srs.g2_powers, 548 | &p1_com, 549 | None, 550 | &[v1_id], 551 | &[proof.v2], 552 | &proof.pi2, 553 | ); 554 | 555 | /////////////////////////////////////////////////////////////////// 556 | // 6. Check pi_3 557 | /////////////////////////////////////////////////////////////////// 558 | 559 | // z_Vm(X) 560 | let zVm: DensePolynomial = srs.domain_m.vanishing_polynomial().into(); // z_V_m(alpah) 561 | 562 | // [P2]_1 = [v2]_1 - chi cm - zVm(alpha) [H_2]_1 563 | let p2_com = ( 564 | srs.poly_ck.powers_of_g[0].mul(proof.v2 ) // [v2]_1 565 | - phi_com.mul( chi ) //[phi]_1 566 | - proof.H2_com.mul(zVm.evaluate(&alpha)) 567 | // [H2]_1 * zVm(alpha) 568 | ) 569 | .into_affine(); 570 | 571 | // KZG.Verify(srs_KZG, [P2]_1, deg = bot, alpha, 0, pi3) 572 | let check3 = KZGCommit::::verify_g1_defer_pairing( 573 | &srs.poly_ck.powers_of_g, 574 | &srs.g2_powers, 575 | &p2_com, 576 | None, 577 | &[alpha], 578 | &[E::Fr::zero()], 579 | &proof.pi3, 580 | ); 581 | 582 | /////////////////////////////////////////////////////////////////// 583 | // 7. prepare final pairing 584 | /////////////////////////////////////////////////////////////////// 585 | 586 | // pairing1 = e([C]_1 - [C_I]_1, [1]_2) 587 | let final_pairing = vec![ 588 | ( 589 | proof.C_I_com.into_projective() - c_com.into_projective(), 590 | srs.g2_powers[0].into_projective(), 591 | ), 592 | ( 593 | proof.z_I_com.into_projective(), 594 | proof.H1_com.into_projective(), 595 | ), 596 | ]; 597 | 598 | /////////////////////////////////////////////////////////////////// 599 | // 7. Check pairing products 600 | /////////////////////////////////////////////////////////////////// 601 | let pairing_timer = start_timer!(|| "pairing product"); 602 | let mut pairing_inputs: Vec<(E::G1Projective, E::G2Projective)> = [ 603 | unity_check.as_slice(), 604 | check1.as_slice(), 605 | check2.as_slice(), 606 | check3.as_slice(), 607 | final_pairing.as_slice(), 608 | ] 609 | .concat(); 610 | 611 | let mut zeta = E::Fr::rand(rng); 612 | let mut prepared_pairing_inputs = vec![]; 613 | for i in 0..pairing_inputs.len() / 2 { 614 | if i != 0 { 615 | pairing_inputs[i * 2].0.mul_assign(zeta); 616 | pairing_inputs[i * 2 + 1].0.mul_assign(zeta); 617 | } 618 | zeta.square_in_place(); 619 | prepared_pairing_inputs.push(( 620 | E::G1Prepared::from(pairing_inputs[i * 2].0.into_affine()), 621 | E::G2Prepared::from(pairing_inputs[i * 2].1.into_affine()), 622 | )); 623 | prepared_pairing_inputs.push(( 624 | E::G1Prepared::from(pairing_inputs[i * 2 + 1].0.into_affine()), 625 | E::G2Prepared::from(pairing_inputs[i * 2 + 1].1.into_affine()), 626 | )); 627 | } 628 | let res = E::product_of_pairings(prepared_pairing_inputs.iter()).is_one(); 629 | 630 | end_timer!(pairing_timer); 631 | end_timer!(timer); 632 | res 633 | } 634 | 635 | #[allow(non_snake_case)] 636 | #[allow(dead_code)] 637 | pub fn generate_lookup_input( 638 | rng: &mut R, 639 | ) -> ( 640 | LookupProverInput, 641 | PublicParameters, // SRS 642 | ) { 643 | let timer = start_timer!(|| "generate lookup input"); 644 | let n: usize = 8; // bitlength of poly degree 645 | let m: usize = 4; 646 | // let m: usize = (1<<(n/2-1)); //should be power of 2 647 | let N: usize = 1 << n; 648 | let max_degree: usize = if N > 2 * m * m { N - 1 } else { 2 * m * m }; 649 | let actual_degree = N - 1; 650 | let now = Instant::now(); 651 | let pp = PublicParameters::::setup(&max_degree, &N, &m, &n); 652 | println!("Time to setup {:?}", now.elapsed()); 653 | 654 | let c_poly = DensePolynomial::::rand(actual_degree, rng); 655 | 656 | let mut positions: Vec = vec![]; 657 | for j in 0..m { 658 | // generate positions evenly distributed in the set 659 | let i_j: usize = j * (N / m); 660 | positions.push(i_j); 661 | } 662 | 663 | // generating phi 664 | let blinder = E::Fr::rand(rng); 665 | let a_m = DensePolynomial::from_coefficients_slice(&[blinder]); 666 | let mut phi_poly = a_m.mul_by_vanishing_poly(pp.domain_m); 667 | for (j, &pos) in positions.iter().enumerate().take(m) { 668 | phi_poly += &(&pp.lagrange_polynomials_m[j] * c_poly.evaluate(&pp.domain_N.element(pos))); 669 | // adding c(w^{i_j})*mu_j(X) 670 | } 671 | 672 | for j in m..pp.domain_m.size() { 673 | phi_poly = 674 | &phi_poly + &(&pp.lagrange_polynomials_m[j] * c_poly.evaluate(&pp.domain_N.element(0))); 675 | } 676 | 677 | let now = Instant::now(); 678 | let openings = KZGCommit::::multiple_open::(&c_poly, &pp.g2_powers, n); 679 | println!("Time to generate openings {:?}", now.elapsed()); 680 | 681 | end_timer!(timer); 682 | ( 683 | LookupProverInput { 684 | c_poly, 685 | phi_poly, 686 | positions, 687 | openings, 688 | }, 689 | pp, 690 | ) 691 | } 692 | 693 | pub fn get_poly_and_g2_openings( 694 | pp: &PublicParameters, 695 | actual_degree: usize, 696 | ) -> TableInput { 697 | // try opening the file. If it exists load the setup from there, otherwise 698 | // generate 699 | let path = format!( 700 | "polys/poly_{}_openings_{}_{}.setup", 701 | actual_degree, 702 | pp.N, 703 | E::Fq::size_in_bits() 704 | ); 705 | let res = File::open(path.clone()); 706 | match res { 707 | Ok(_) => { 708 | let now = Instant::now(); 709 | let table = TableInput::load(&path); 710 | println!("Time to load openings = {:?}", now.elapsed()); 711 | table 712 | }, 713 | Err(_) => { 714 | let rng = &mut ark_std::test_rng(); 715 | let c_poly = DensePolynomial::::rand(actual_degree, rng); 716 | let c_comx = KZGCommit::::commit_g1(&pp.poly_ck, &c_poly); 717 | let now = Instant::now(); 718 | let openings = 719 | KZGCommit::::multiple_open::(&c_poly, &pp.g2_powers, pp.n); 720 | println!("Time to generate openings = {:?}", now.elapsed()); 721 | let table = TableInput { 722 | c_poly, 723 | c_com: c_comx, 724 | openings, 725 | }; 726 | table.store(&path); 727 | table 728 | }, 729 | } 730 | } 731 | 732 | #[cfg(test)] 733 | mod tests { 734 | use super::*; 735 | use ark_bls12_377::Bls12_377; 736 | use ark_bls12_381::Bls12_381; 737 | use ark_ff::PrimeField; 738 | 739 | #[test] 740 | fn test_store() { 741 | test_store_helper::(); 742 | test_store_helper::(); 743 | } 744 | 745 | #[allow(non_snake_case)] 746 | pub fn test_store_helper() { 747 | // 1. Setup 748 | let n: usize = 6; 749 | let N: usize = 1 << n; 750 | let powers_size: usize = N + 2; // SRS SIZE 751 | let temp_m = n; // dummy 752 | let pp = PublicParameters::::setup(&powers_size, &N, &temp_m, &n); 753 | let actual_degree = N - 1; 754 | let path = format!("tmp/poly_openings_{}.log", E::Fq::size_in_bits()); 755 | 756 | // 2. Store 757 | let rng = &mut ark_std::test_rng(); 758 | let c_poly = DensePolynomial::::rand(actual_degree, rng); 759 | let c_com = KZGCommit::::commit_g1(&pp.poly_ck, &c_poly); 760 | let openings = KZGCommit::::multiple_open::(&c_poly, &pp.g2_powers, pp.n); 761 | let table = TableInput:: { 762 | c_poly, 763 | c_com, 764 | openings, 765 | }; 766 | table.store(&path); 767 | 768 | // 3. Load 769 | let table_loaded = TableInput::load(&path); 770 | 771 | // 4. Test 772 | assert_eq!(table, table_loaded); 773 | std::fs::remove_file(&path).expect("File can not be deleted"); 774 | } 775 | 776 | #[allow(non_snake_case)] 777 | #[test] 778 | fn test_multiple_lookups() { 779 | do_multiple_lookups::(); 780 | do_multiple_lookups::(); 781 | } 782 | 783 | #[allow(non_snake_case)] 784 | fn do_multiple_lookups() { 785 | let mut rng = ark_std::test_rng(); 786 | 787 | const MIN_LOG_N: usize = 7; 788 | const MAX_LOG_N: usize = 9; 789 | const EPS: usize = 1; 790 | const MIN_LOG_M: usize = 2; 791 | const MAX_LOG_M: usize = 5; 792 | 793 | for n in MIN_LOG_N..=MAX_LOG_N { 794 | // 1. Setup 795 | let N: usize = 1 << n; 796 | let powers_size: usize = N + 2; // SRS SIZE 797 | println!("N={}", N); 798 | let temp_m = n; // dummy 799 | let mut pp = PublicParameters::::setup(&powers_size, &N, &temp_m, &n); 800 | let actual_degree = N - EPS; 801 | // println!("time for powers of tau {:?} for N={:?}", now.elapsed(),N); 802 | 803 | // 2. Poly and openings 804 | let table = get_poly_and_g2_openings(&pp, actual_degree); 805 | 806 | for logm in MIN_LOG_M..=std::cmp::min(MAX_LOG_M, n / 2 - 1) { 807 | // 3. Setup 808 | let now = Instant::now(); 809 | let mut m = 1 << logm; 810 | m = m + 1; 811 | 812 | println!("m={}", m); 813 | pp.regenerate_lookup_params(m); 814 | println!("Time to generate aux domain {:?}", now.elapsed()); 815 | 816 | // 4. Positions 817 | let mut positions: Vec = vec![]; 818 | for j in 0..m { 819 | // generate positions evenly distributed in the set 820 | let i_j: usize = j * (actual_degree / m); 821 | positions.push(i_j); 822 | } 823 | 824 | // 5. generating phi 825 | let blinder = E::Fr::rand(&mut rng); 826 | let a_m = DensePolynomial::from_coefficients_slice(&[blinder]); 827 | let mut phi_poly = a_m.mul_by_vanishing_poly(pp.domain_m); 828 | let c_poly_local = table.c_poly.clone(); 829 | for j in 0..m { 830 | phi_poly = &phi_poly 831 | + &(&pp.lagrange_polynomials_m[j] 832 | * c_poly_local.evaluate(&pp.domain_N.element(positions[j]))); 833 | // adding c(w^{i_j})*mu_j(X) 834 | } 835 | 836 | for j in m..pp.domain_m.size() { 837 | phi_poly = &phi_poly 838 | + &(&pp.lagrange_polynomials_m[j] 839 | * c_poly_local.evaluate(&pp.domain_N.element(0))); 840 | // adding c(w^{i_j})*mu_j(X) 841 | } 842 | 843 | // 6. Running proofs 844 | let now = Instant::now(); 845 | let c_com = KZGCommit::::commit_g1(&pp.poly_ck, &table.c_poly); 846 | let phi_com = KZGCommit::::commit_g1(&pp.poly_ck, &phi_poly); 847 | println!("Time to generate inputs = {:?}", now.elapsed()); 848 | 849 | let lookup_instance = LookupInstance { c_com, phi_com }; 850 | 851 | let prover_input = LookupProverInput { 852 | c_poly: table.c_poly.clone(), 853 | phi_poly, 854 | positions, 855 | openings: table.openings.clone(), 856 | }; 857 | 858 | let now = Instant::now(); 859 | let (proof, unity_proof) = 860 | compute_lookup_proof::(&lookup_instance, &prover_input, &pp, &mut rng); 861 | println!("Time to generate proof for = {:?}", now.elapsed()); 862 | let now = Instant::now(); 863 | let res = verify_lookup_proof( 864 | &table.c_com, 865 | &phi_com, 866 | &proof, 867 | &unity_proof, 868 | &pp, 869 | &mut rng, 870 | ); 871 | println!("Time to verify proof for = {:?}", now.elapsed()); 872 | assert!(res); 873 | println!("Lookup test succeeded"); 874 | } 875 | } 876 | } 877 | 878 | #[allow(non_snake_case)] 879 | #[test] 880 | fn test_lookup() { 881 | do_lookup::(); 882 | do_lookup::(); 883 | } 884 | 885 | fn do_lookup() { 886 | let mut rng = ark_std::test_rng(); 887 | 888 | let now = Instant::now(); 889 | let (prover_input, srs) = generate_lookup_input(&mut rng); 890 | println!( 891 | "Time to generate parameters for n={:?} = {:?}", 892 | srs.n, 893 | now.elapsed() 894 | ); 895 | // kzg_test(&srs); 896 | let c_com = KZGCommit::::commit_g1(&srs.poly_ck, &prover_input.c_poly); 897 | let phi_com = KZGCommit::::commit_g1(&srs.poly_ck, &prover_input.phi_poly); 898 | 899 | let lookup_instance = LookupInstance { c_com, phi_com }; 900 | 901 | let now = Instant::now(); 902 | let (proof, unity_proof) = 903 | compute_lookup_proof(&lookup_instance, &prover_input, &srs, &mut rng); 904 | println!( 905 | "Time to generate proof for m={:?} = {:?}", 906 | srs.m, 907 | now.elapsed() 908 | ); 909 | let now = Instant::now(); 910 | let res = verify_lookup_proof(&c_com, &phi_com, &proof, &unity_proof, &srs, &mut rng); 911 | println!( 912 | "Time to verify proof for n={:?} = {:?}", 913 | srs.n, 914 | now.elapsed() 915 | ); 916 | assert!(res); 917 | println!("Lookup test succeeded"); 918 | } 919 | } 920 | -------------------------------------------------------------------------------- /src/multi/setup.rs: -------------------------------------------------------------------------------- 1 | use crate::util::trim; 2 | use ark_ec::{AffineCurve, PairingEngine, ProjectiveCurve}; 3 | use ark_ff::{PrimeField, UniformRand}; 4 | use ark_poly::{ 5 | univariate::DensePolynomial, EvaluationDomain, Evaluations as EvaluationsOnDomain, 6 | GeneralEvaluationDomain, 7 | }; 8 | use ark_poly_commit::kzg10::*; 9 | use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; 10 | use ark_std::{cfg_into_iter, One, Zero}; 11 | #[cfg(feature = "parallel")] 12 | use rayon::iter::{IntoParallelIterator, ParallelIterator}; 13 | use std::{ 14 | convert::TryInto, 15 | fs::File, 16 | io::{Read, Write}, 17 | time::Instant, 18 | }; 19 | 20 | // structure of public parameters 21 | #[allow(non_snake_case)] 22 | pub struct PublicParameters { 23 | pub poly_ck: Powers<'static, E>, 24 | pub domain_m: GeneralEvaluationDomain, 25 | pub domain_n: GeneralEvaluationDomain, 26 | pub domain_N: GeneralEvaluationDomain, 27 | pub verifier_pp: VerifierPublicParameters, 28 | pub lagrange_polynomials_n: Vec>, 29 | pub lagrange_polynomials_m: Vec>, 30 | pub id_poly: DensePolynomial, 31 | pub N: usize, 32 | pub m: usize, 33 | pub n: usize, 34 | pub g2_powers: Vec, 35 | } 36 | 37 | pub struct LookupParameters { 38 | m: usize, 39 | lagrange_polynomials_m: Vec>, 40 | domain_m: GeneralEvaluationDomain, 41 | id_poly: DensePolynomial, 42 | } 43 | 44 | impl LookupParameters { 45 | fn new(m: usize) -> Self { 46 | let domain_m: GeneralEvaluationDomain = GeneralEvaluationDomain::new(m).unwrap(); 47 | 48 | // id_poly(X) = 1 for omega_m in range and 0 for omega_m not in range. 49 | let mut id_vec = Vec::new(); 50 | for _ in 0..m { 51 | id_vec.push(F::one()); 52 | } 53 | for _ in m..domain_m.size() { 54 | id_vec.push(F::zero()); 55 | } 56 | let id_poly = EvaluationsOnDomain::from_vec_and_domain(id_vec, domain_m).interpolate(); 57 | let mut lagrange_polynomials_m: Vec> = Vec::new(); 58 | 59 | for i in 0..domain_m.size() { 60 | let evals: Vec = cfg_into_iter!(0..domain_m.size()) 61 | .map(|k| if k == i { F::one() } else { F::zero() }) 62 | .collect(); 63 | lagrange_polynomials_m 64 | .push(EvaluationsOnDomain::from_vec_and_domain(evals, domain_m).interpolate()); 65 | } 66 | 67 | Self { 68 | m, 69 | lagrange_polynomials_m, 70 | domain_m, 71 | id_poly, 72 | } 73 | } 74 | } 75 | 76 | // smaller set of public parameters used by verifier 77 | pub struct VerifierPublicParameters { 78 | pub poly_vk: VerifierKey, 79 | pub domain_m_size: usize, 80 | } 81 | 82 | impl PublicParameters { 83 | pub fn regenerate_lookup_params(&mut self, m: usize) { 84 | let lp = LookupParameters::new(m); 85 | self.m = lp.m; 86 | self.lagrange_polynomials_m = lp.lagrange_polynomials_m; 87 | self.domain_m = lp.domain_m; 88 | self.id_poly = lp.id_poly; 89 | } 90 | 91 | // store powers of g in a file 92 | pub fn store(&self, path: &str) { 93 | // 1. Powers of g 94 | let mut g_bytes = vec![]; 95 | let mut f = File::create(path).expect("Unable to create file"); 96 | let deg: u32 = self.poly_ck.powers_of_g.len().try_into().unwrap(); 97 | let deg_bytes = deg.to_be_bytes(); 98 | f.write_all(°_bytes).expect("Unable to write data"); 99 | let deg32: usize = deg.try_into().unwrap(); 100 | for i in 0..deg32 { 101 | self.poly_ck.powers_of_g[i] 102 | .into_projective() 103 | .into_affine() 104 | .serialize_uncompressed(&mut g_bytes) 105 | .ok(); 106 | } 107 | f.write_all(&g_bytes).expect("Unable to write data"); 108 | 109 | // 2. Powers of gammag 110 | let deg_gamma: u32 = self.poly_ck.powers_of_gamma_g.len().try_into().unwrap(); 111 | let mut gg_bytes = vec![]; 112 | let deg_bytes = deg_gamma.to_be_bytes(); 113 | f.write_all(°_bytes).expect("Unable to write data"); 114 | let deg32: usize = deg.try_into().unwrap(); 115 | for i in 0..deg32 { 116 | self.poly_ck.powers_of_gamma_g[i] 117 | .into_projective() 118 | .into_affine() 119 | .serialize_uncompressed(&mut gg_bytes) 120 | .ok(); 121 | } 122 | f.write_all(&gg_bytes).expect("Unable to write data"); 123 | 124 | // 3. Verifier key 125 | let mut h_bytes = vec![]; 126 | self.verifier_pp 127 | .poly_vk 128 | .h 129 | .serialize_uncompressed(&mut h_bytes) 130 | .ok(); 131 | self.verifier_pp 132 | .poly_vk 133 | .beta_h 134 | .serialize_uncompressed(&mut h_bytes) 135 | .ok(); 136 | f.write_all(&h_bytes).expect("Unable to write data"); 137 | 138 | // 4. g2 powers 139 | let mut g2_bytes = vec![]; 140 | let deg2: u32 = self.g2_powers.len().try_into().unwrap(); 141 | let deg2_bytes = deg2.to_be_bytes(); 142 | f.write_all(°2_bytes).expect("Unable to write data"); 143 | let deg2_32: usize = deg2.try_into().unwrap(); 144 | for i in 0..deg2_32 { 145 | self.g2_powers[i] 146 | .into_projective() 147 | .into_affine() 148 | .serialize_uncompressed(&mut g2_bytes) 149 | .ok(); 150 | } 151 | f.write_all(&g2_bytes).expect("Unable to write data"); 152 | } 153 | 154 | // load powers of g from a file 155 | pub fn load(path: &str) -> (Powers<'static, E>, VerifierKey, Vec) { 156 | const G1_UNCOMPR_SIZE: usize = 96; 157 | const G2_UNCOMPR_SIZE: usize = 192; 158 | let mut data = Vec::new(); 159 | let mut f = File::open(path).expect("Unable to open file"); 160 | f.read_to_end(&mut data).expect("Unable to read data"); 161 | 162 | // 1. reading g powers 163 | let mut cur_counter: usize = 0; 164 | let deg_bytes: [u8; 4] = (&data[0..4]).try_into().unwrap(); 165 | let deg: u32 = u32::from_be_bytes(deg_bytes); 166 | let mut powers_of_g = vec![]; 167 | let deg32: usize = deg.try_into().unwrap(); 168 | cur_counter += 4; 169 | for i in 0..deg32 { 170 | let buf_bytes = 171 | &data[cur_counter + i * G1_UNCOMPR_SIZE..cur_counter + (i + 1) * G1_UNCOMPR_SIZE]; 172 | let tmp = E::G1Affine::deserialize_unchecked(buf_bytes).unwrap(); 173 | powers_of_g.push(tmp); 174 | } 175 | cur_counter += deg32 * G1_UNCOMPR_SIZE; 176 | 177 | // 2. reading gamma g powers 178 | let deg_bytes: [u8; 4] = (&data[cur_counter..cur_counter + 4]).try_into().unwrap(); 179 | let deg: u32 = u32::from_be_bytes(deg_bytes); 180 | let mut powers_of_gamma_g = vec![]; 181 | let deg32: usize = deg.try_into().unwrap(); 182 | cur_counter += 4; 183 | for i in 0..deg32 { 184 | let buf_bytes = 185 | &data[cur_counter + i * G1_UNCOMPR_SIZE..cur_counter + (i + 1) * G1_UNCOMPR_SIZE]; 186 | let tmp = E::G1Affine::deserialize_unchecked(buf_bytes).unwrap(); 187 | powers_of_gamma_g.push(tmp); 188 | } 189 | cur_counter += deg32 * G1_UNCOMPR_SIZE; 190 | 191 | // 3. reading verifier key 192 | let buf_bytes = &data[cur_counter..cur_counter + G2_UNCOMPR_SIZE]; 193 | let h = E::G2Affine::deserialize_unchecked(buf_bytes).unwrap(); 194 | cur_counter += G2_UNCOMPR_SIZE; 195 | let buf_bytes = &data[cur_counter..cur_counter + G2_UNCOMPR_SIZE]; 196 | let beta_h = E::G2Affine::deserialize_unchecked(buf_bytes).unwrap(); 197 | cur_counter += G2_UNCOMPR_SIZE; 198 | 199 | // 4. reading G2 powers 200 | let deg2_bytes: [u8; 4] = (&data[cur_counter..cur_counter + 4]).try_into().unwrap(); 201 | let deg2: u32 = u32::from_be_bytes(deg2_bytes); 202 | let mut g2_powers = vec![]; 203 | let deg2_32: usize = deg2.try_into().unwrap(); 204 | cur_counter += 4; 205 | for _ in 0..deg2_32 { 206 | let buf_bytes = &data[cur_counter..cur_counter + G2_UNCOMPR_SIZE]; 207 | let tmp = E::G2Affine::deserialize_unchecked(buf_bytes).unwrap(); 208 | g2_powers.push(tmp); 209 | cur_counter += G2_UNCOMPR_SIZE; 210 | } 211 | 212 | let vk = VerifierKey { 213 | g: powers_of_g[0], 214 | gamma_g: powers_of_gamma_g[0], 215 | h, 216 | beta_h, 217 | prepared_h: h.into(), 218 | prepared_beta_h: beta_h.into(), 219 | }; 220 | 221 | let powers = Powers { 222 | powers_of_g: ark_std::borrow::Cow::Owned(powers_of_g), 223 | powers_of_gamma_g: ark_std::borrow::Cow::Owned(powers_of_gamma_g), 224 | }; 225 | 226 | (powers, vk, g2_powers) 227 | } 228 | 229 | // setup algorithm for index_hiding_polycommit 230 | // also includes a bunch of precomputation. 231 | // @max_degree max degree of table polynomial C(X), also the size of the trusted 232 | // setup @N domain size on which proofs are constructed. Should not be 233 | // smaller than max_degree @m lookup size. Can be changed later 234 | // @n suppl domain for the unity proofs. Should be at least 6+log N 235 | #[allow(non_snake_case)] 236 | pub fn setup(max_degree: &usize, N: &usize, m: &usize, n: &usize) -> PublicParameters { 237 | // Setup algorithm. To be replaced by output of a universal setup before being 238 | // production ready. 239 | 240 | // let mut srs = KzgBls12_381::setup(4, true, rng).unwrap(); 241 | let poly_ck: Powers<'static, E>; 242 | let poly_vk: VerifierKey; 243 | let mut g2_powers: Vec = Vec::new(); 244 | 245 | // try opening the file. If it exists load the setup from there, otherwise 246 | // generate 247 | let path = format!("srs/srs_{}_{}.setup", max_degree, E::Fq::size_in_bits()); 248 | let res = File::open(path.clone()); 249 | let store_to_file: bool; 250 | match res { 251 | Ok(_) => { 252 | let now = Instant::now(); 253 | let (_poly_ck, _poly_vk, _g2_powers) = PublicParameters::load(&path); 254 | println!("time to load powers = {:?}", now.elapsed()); 255 | store_to_file = false; 256 | g2_powers = _g2_powers; 257 | poly_ck = _poly_ck; 258 | poly_vk = _poly_vk; 259 | }, 260 | Err(_) => { 261 | let rng = &mut ark_std::test_rng(); 262 | let now = Instant::now(); 263 | let srs = 264 | KZG10::>::setup(*max_degree, true, rng).unwrap(); 265 | println!("time to setup powers = {:?}", now.elapsed()); 266 | 267 | // trim down to size 268 | let (poly_ck2, poly_vk2) = trim::>(&srs, *max_degree); 269 | poly_ck = Powers { 270 | powers_of_g: ark_std::borrow::Cow::Owned(poly_ck2.powers_of_g.into()), 271 | powers_of_gamma_g: ark_std::borrow::Cow::Owned( 272 | poly_ck2.powers_of_gamma_g.into(), 273 | ), 274 | }; 275 | poly_vk = poly_vk2; 276 | 277 | // need some powers of g2 278 | // arkworks setup doesn't give these powers but the setup does use a fixed 279 | // randomness to generate them. so we can generate powers of g2 280 | // directly. 281 | let beta = E::Fr::rand(rng); 282 | let mut temp = poly_vk.h; 283 | 284 | for _ in 0..poly_ck.powers_of_g.len() { 285 | g2_powers.push(temp); 286 | temp = temp.mul(beta).into_affine(); 287 | } 288 | 289 | store_to_file = true; 290 | }, 291 | } 292 | 293 | // domain where openings {w_i}_{i in I} are embedded 294 | let domain_n: GeneralEvaluationDomain = GeneralEvaluationDomain::new(*n).unwrap(); 295 | let domain_N: GeneralEvaluationDomain = GeneralEvaluationDomain::new(*N).unwrap(); 296 | 297 | // precomputation to speed up prover 298 | // lagrange_polynomials[i] = polynomial equal to 0 at w^j for j!= i and 1 at 299 | // w^i 300 | let mut lagrange_polynomials_n: Vec> = Vec::new(); 301 | 302 | for i in 0..domain_n.size() { 303 | let evals: Vec = cfg_into_iter!(0..domain_n.size()) 304 | .map(|k| if k == i { E::Fr::one() } else { E::Fr::zero() }) 305 | .collect(); 306 | lagrange_polynomials_n 307 | .push(EvaluationsOnDomain::from_vec_and_domain(evals, domain_n).interpolate()); 308 | } 309 | 310 | let lp = LookupParameters::new(*m); 311 | 312 | let verifier_pp = VerifierPublicParameters { 313 | poly_vk, 314 | domain_m_size: lp.domain_m.size(), 315 | }; 316 | 317 | let pp = PublicParameters { 318 | poly_ck, 319 | domain_m: lp.domain_m, 320 | domain_n, 321 | lagrange_polynomials_n, 322 | lagrange_polynomials_m: lp.lagrange_polynomials_m, 323 | id_poly: lp.id_poly, 324 | domain_N, 325 | verifier_pp, 326 | N: *N, 327 | n: *n, 328 | m: lp.m, 329 | g2_powers, 330 | }; 331 | if store_to_file { 332 | pp.store(&path); 333 | } 334 | pp 335 | } 336 | } 337 | 338 | #[test] 339 | #[allow(non_snake_case)] 340 | pub fn test_load() { 341 | use ark_bls12_381::Bls12_381; 342 | 343 | let n: usize = 4; 344 | let N: usize = 1 << n; 345 | let powers_size: usize = 4 * N; // SRS SIZE 346 | let temp_m = n; // dummy 347 | let pp = PublicParameters::::setup(&powers_size, &N, &temp_m, &n); 348 | let path = "powers.log"; 349 | pp.store(path); 350 | let loaded = PublicParameters::::load(path); 351 | assert_eq!(pp.poly_ck.powers_of_g, loaded.0.powers_of_g); 352 | assert_eq!(pp.poly_ck.powers_of_gamma_g, loaded.0.powers_of_gamma_g); 353 | assert_eq!(pp.verifier_pp.poly_vk.h, loaded.1.h); 354 | assert_eq!(pp.verifier_pp.poly_vk.beta_h, loaded.1.beta_h); 355 | assert_eq!(pp.g2_powers, loaded.2); 356 | std::fs::remove_file(&path).expect("File can not be deleted"); 357 | } 358 | -------------------------------------------------------------------------------- /src/multi/unity.rs: -------------------------------------------------------------------------------- 1 | // This file includes the Caulk's unity prover and verifier for multi openings. 2 | // The protocol is described in Figure 4. 3 | 4 | use super::setup::PublicParameters; 5 | use crate::{util::convert_to_bigints, CaulkTranscript, KZGCommit}; 6 | use ark_ec::{msm::VariableBaseMSM, AffineCurve, PairingEngine, ProjectiveCurve}; 7 | use ark_ff::Field; 8 | use ark_poly::{ 9 | univariate::DensePolynomial, EvaluationDomain, Evaluations as EvaluationsOnDomain, Polynomial, 10 | UVPolynomial, 11 | }; 12 | use ark_std::{end_timer, start_timer, One, UniformRand, Zero}; 13 | use rand::RngCore; 14 | use std::ops::MulAssign; 15 | 16 | // output structure of prove_unity 17 | pub struct ProofMultiUnity { 18 | pub g1_u_bar: E::G1Affine, 19 | pub g1_h_1: E::G1Affine, 20 | pub g1_h_2: E::G1Affine, 21 | pub g1_u_bar_alpha: E::G1Affine, 22 | pub g1_h_2_alpha: E::G1Affine, 23 | pub v1: E::Fr, 24 | pub v2: E::Fr, 25 | pub v3: E::Fr, 26 | pub pi_1: E::G1Affine, 27 | pub pi_2: E::G1Affine, 28 | pub pi_3: E::G1Affine, 29 | pub pi_4: E::G1Affine, 30 | pub pi_5: E::G1Affine, 31 | } 32 | 33 | // Prove knowledge of vec_u_evals such that g1_u = g1^(sum_j u_j mu_j(x)) and 34 | // u_j^N = 1 35 | #[allow(non_snake_case)] 36 | pub fn prove_multiunity( 37 | pp: &PublicParameters, 38 | transcript: &mut CaulkTranscript, 39 | g1_u: &E::G1Affine, 40 | vec_u_evals: &[E::Fr], 41 | u_poly_quotient: DensePolynomial, 42 | ) -> ProofMultiUnity { 43 | let timer = start_timer!(|| "prove multiunity"); 44 | // The test_rng is deterministic. Should be replaced with actual random 45 | // generator. 46 | let rng_arkworks = &mut ark_std::test_rng(); 47 | 48 | let n = pp.n; 49 | let deg_blinders = 11 / n; 50 | let z_Vm: DensePolynomial = pp.domain_m.vanishing_polynomial().into(); 51 | let mut vec_u_evals = vec_u_evals.to_vec(); 52 | 53 | ////////////////////////////////////////////////////////////////////////////////////////////////////////// 54 | // 1. Compute polynomials u_s(X) = vec_u_polys[s] such that u_s( nu_i ) = 55 | // w_i^{2^s} 56 | ////////////////////////////////////////////////////////////////////////////////////////////////////////// 57 | let step1_timer = start_timer!(|| "step 1"); 58 | let mut vec_u_polys = vec![ 59 | EvaluationsOnDomain::from_vec_and_domain(vec_u_evals.to_vec(), pp.domain_m).interpolate() 60 | + (&z_Vm * &u_poly_quotient), 61 | ]; 62 | 63 | for _ in 1..pp.domain_n.size() { 64 | for u_eval in vec_u_evals.iter_mut() { 65 | *u_eval = u_eval.square(); 66 | } 67 | 68 | vec_u_polys.push( 69 | EvaluationsOnDomain::from_vec_and_domain(vec_u_evals.to_vec(), pp.domain_m) 70 | .interpolate() 71 | + (&z_Vm * &DensePolynomial::::rand(deg_blinders, rng_arkworks)), 72 | ); 73 | } 74 | end_timer!(step1_timer); 75 | ////////////////////////////////////////////////////////////////////////////////////////////////////////// 76 | // 2. Compute U_bar(X,Y) = sum_{s= 1}^n u_{s-1} rho_s(Y) 77 | ////////////////////////////////////////////////////////////////////////////////////////////////////////// 78 | let step2_timer = start_timer!(|| "step 2"); 79 | // bivariate polynomials such that bipoly_U_bar[j] = a_j(Y) where U_bar(X,Y) = 80 | // sum_j X^j a_j(Y) 81 | let mut bipoly_U_bar = Vec::new(); 82 | 83 | // vec_u_polys[0] has an extended degree because it is blinded so use 84 | // vec_u_polys[1] for the length 85 | for j in 0..vec_u_polys[1].len() { 86 | /* 87 | Denoting u_{s-1}(X) = sum_j u_{s-1, j} X^j then 88 | temp is a_j(Y) = sum_{s=1}^n u_{s-1, j} * rho_s(Y) 89 | */ 90 | let mut temp = DensePolynomial::from_coefficients_slice(&[E::Fr::zero()]); 91 | 92 | for (s, u_poly) in vec_u_polys.iter().enumerate().take(n).skip(1) { 93 | let u_s_j = DensePolynomial::from_coefficients_slice(&[u_poly[j]]); 94 | temp += &(&u_s_j * &pp.lagrange_polynomials_n[s]); 95 | } 96 | 97 | // add a_j(X) to U_bar(X,Y) 98 | bipoly_U_bar.push(temp); 99 | } 100 | end_timer!(step2_timer); 101 | ////////////////////////////////////////////////////////////////////////////////////////////////////////// 102 | // 3. Hs(X) = u_{s-1}^2(X) - u_s(X) 103 | ////////////////////////////////////////////////////////////////////////////////////////////////////////// 104 | let step3_timer = start_timer!(|| "step 3"); 105 | // id_poly(X) = 1 for omega_m in range and 0 for omega_m not in range. 106 | let id_poly = pp.id_poly.clone(); 107 | 108 | // Hs(X) = (u_{s-1}^2(X) - u_s(X)) / zVm(X). Abort if doesn't divide. 109 | let mut vec_H_s_polys: Vec> = Vec::new(); 110 | for s in 1..n { 111 | let (poly_H_s, remainder) = (&(&vec_u_polys[s - 1] * &vec_u_polys[s - 1]) 112 | - &vec_u_polys[s]) 113 | .divide_by_vanishing_poly(pp.domain_m) 114 | .unwrap(); 115 | assert!(remainder.is_zero()); 116 | vec_H_s_polys.push(poly_H_s); 117 | } 118 | 119 | // Hn(X) = u_{n-1}^2(X) - id(X) / zVm(X). Abort if doesn't divide. 120 | let (poly_H_s, remainder) = (&(&vec_u_polys[n - 1] * &vec_u_polys[n - 1]) - &id_poly) 121 | .divide_by_vanishing_poly(pp.domain_m) 122 | .unwrap(); 123 | assert!(remainder.is_zero()); 124 | vec_H_s_polys.push(poly_H_s); 125 | end_timer!(step3_timer); 126 | ////////////////////////////////////////////////////////////////////////////////////////////////////////// 127 | // 4. h_2(X,Y) = sum_{s=1}^n rho_s(Y) H_s(X) 128 | ////////////////////////////////////////////////////////////////////////////////////////////////////////// 129 | let step4_timer = start_timer!(|| "step 4"); 130 | // h_2[j] = a_j(Y) where h_2(X,Y) = sum_j X^j a_j(Y) 131 | let mut bipoly_h_2 = Vec::new(); 132 | 133 | // first add H_1(X) rho_1(Y) 134 | for j in 0..vec_H_s_polys[0].len() { 135 | let h_0_j = DensePolynomial::from_coefficients_slice(&[vec_H_s_polys[0][j]]); 136 | bipoly_h_2.push(&h_0_j * &pp.lagrange_polynomials_n[0]); 137 | } 138 | 139 | // In case length of H_1(X) and H_2(X) is different pad with zeros. 140 | for _ in vec_H_s_polys[0].len()..vec_H_s_polys[1].len() { 141 | let h_0_j = DensePolynomial::from_coefficients_slice(&[E::Fr::zero()]); 142 | bipoly_h_2.push(h_0_j); 143 | } 144 | 145 | // h_2(X,Y) = sum_j ( sum_s H_{s,j} * rho_s(Y) ) X^j 146 | for (j, coeff) in bipoly_h_2 147 | .iter_mut() 148 | .enumerate() 149 | .take(vec_H_s_polys[1].len()) 150 | { 151 | // h_2[j] = sum_s H_{s,j} * rho_s(Y) 152 | for (s, H_s_poly) in vec_H_s_polys.iter().enumerate().take(n).skip(1) { 153 | let h_s_j = DensePolynomial::from_coefficients_slice(&[H_s_poly[j]]); 154 | 155 | // h_2[j] += H_{s,j} * rho_s(Y) 156 | *coeff += &(&h_s_j * &pp.lagrange_polynomials_n[s]); 157 | } 158 | } 159 | end_timer!(step4_timer); 160 | ////////////////////////////////////////////////////////////////////////////////////////////////////////// 161 | // 5. Commit to U_bar(X^n, X) and h_2(X^n, X) 162 | ////////////////////////////////////////////////////////////////////////////////////////////////////////// 163 | let step5_timer = start_timer!(|| "step 5"); 164 | let g1_u_bar = KZGCommit::::bipoly_commit(pp, &bipoly_U_bar, pp.domain_n.size()); 165 | let g1_h_2 = KZGCommit::::bipoly_commit(pp, &bipoly_h_2, pp.domain_n.size()); 166 | end_timer!(step5_timer); 167 | //////////////////////////// 168 | // 6. alpha = Hash(g1_u, g1_u_bar, g1_h_2) 169 | //////////////////////////// 170 | let step6_timer = start_timer!(|| "step 6"); 171 | transcript.append_element(b"u", g1_u); 172 | transcript.append_element(b"u_bar", &g1_u_bar); 173 | transcript.append_element(b"h2", &g1_h_2); 174 | let alpha = transcript.get_and_append_challenge(b"alpha"); 175 | end_timer!(step6_timer); 176 | ////////////////////////////////////////////////////////////////////////////////////////////////////////// 177 | // 7. Compute h_1(Y) 178 | ////////////////////////////////////////////////////////////////////////////////////////////////////////// 179 | let step7_timer = start_timer!(|| "step 7"); 180 | // poly_U_alpha = sum_{s=1}^n u_{s-1}(alpha) rho_s(Y) 181 | let mut poly_U_alpha = DensePolynomial::from_coefficients_slice(&[E::Fr::zero()]); 182 | 183 | // poly_Usq_alpha = sum_{s=1}^n u_{s-1}^2(alpha) rho_s(Y) 184 | let mut poly_Usq_alpha = DensePolynomial::from_coefficients_slice(&[E::Fr::zero()]); 185 | 186 | for (s, u_poly) in vec_u_polys.iter().enumerate().take(n) { 187 | let u_s_alpha = u_poly.evaluate(&alpha); 188 | let mut temp = DensePolynomial::from_coefficients_slice(&[u_s_alpha]); 189 | poly_U_alpha += &(&temp * &pp.lagrange_polynomials_n[s]); 190 | 191 | temp = DensePolynomial::from_coefficients_slice(&[u_s_alpha.square()]); 192 | poly_Usq_alpha += &(&temp * &pp.lagrange_polynomials_n[s]); 193 | } 194 | 195 | // divide h1(Y) = [ U^2(alpha,Y) - sum_{s=1}^n u_{s-1}^2(alpha) rho_s(Y) ) ] / 196 | // zVn(Y) return an error if division fails 197 | let (poly_h_1, remainder) = (&(&poly_U_alpha * &poly_U_alpha) - &poly_Usq_alpha) 198 | .divide_by_vanishing_poly(pp.domain_n) 199 | .unwrap(); 200 | assert!(remainder.is_zero(), "poly_h_1 does not divide"); 201 | end_timer!(step7_timer); 202 | ////////////////////////////////////////////////////////////////////////////////////////////////////////// 203 | // 8. Commit to h_1(Y) 204 | ////////////////////////////////////////////////////////////////////////////////////////////////////////// 205 | let step8_timer = start_timer!(|| "step 8"); 206 | assert!(pp.poly_ck.powers_of_g.len() >= poly_h_1.len()); 207 | let g1_h_1 = VariableBaseMSM::multi_scalar_mul( 208 | &pp.poly_ck.powers_of_g, 209 | convert_to_bigints(&poly_h_1.coeffs).as_slice(), 210 | ) 211 | .into_affine(); 212 | end_timer!(step8_timer); 213 | //////////////////////////// 214 | // 9. beta = Hash( g1_h_1 ) 215 | //////////////////////////// 216 | let step9_timer = start_timer!(|| "step 9"); 217 | transcript.append_element(b"h1", &g1_h_1); 218 | let beta = transcript.get_and_append_challenge(b"beta"); 219 | end_timer!(step9_timer); 220 | ////////////////////////////////////////////////////////////////////////////////////////////////////////// 221 | // 10. Compute p(Y) = (U^2(alpha, beta) - h1(Y) zVn(beta) ) - (u_bar(alpha, beta 222 | // sigma^(-1)) + id(alpha) rho_n(Y)) - zVm(alpha )h2(alpha,Y) 223 | ////////////////////////////////////////////////////////////////////////////////////////////////////////// 224 | 225 | let step10_timer = start_timer!(|| "step 10"); 226 | // p(Y) = U^2(alpha, beta) 227 | let u_alpha_beta = poly_U_alpha.evaluate(&beta); 228 | let mut poly_p = DensePolynomial::from_coefficients_slice(&[u_alpha_beta.square()]); 229 | 230 | //////////////////////////// 231 | // p(Y) = p(Y) - ( u_bar(alpha, beta sigma) + id(alpha) rho_n(beta)) 232 | 233 | // u_bar_alpha_shiftbeta = u_bar(alpha, beta sigma) 234 | let mut u_bar_alpha_shiftbeta = E::Fr::zero(); 235 | let beta_shift = beta * pp.domain_n.element(1); 236 | for (s, u_ploy) in vec_u_polys.iter().enumerate().take(n).skip(1) { 237 | let u_s_alpha = u_ploy.evaluate(&alpha); 238 | u_bar_alpha_shiftbeta += u_s_alpha * pp.lagrange_polynomials_n[s].evaluate(&beta_shift); 239 | } 240 | 241 | // temp = u_bar(alpha, beta sigma) + id(alpha) rho_n(beta) 242 | let temp = u_bar_alpha_shiftbeta 243 | + (id_poly.evaluate(&alpha) * pp.lagrange_polynomials_n[n - 1].evaluate(&beta)); 244 | let temp = DensePolynomial::from_coefficients_slice(&[temp]); 245 | 246 | poly_p = &poly_p - &temp; 247 | 248 | //////////////////////////// 249 | // p(Y) = p(Y) - h1(Y) zVn(beta) 250 | let z_Vn: DensePolynomial = pp.domain_n.vanishing_polynomial().into(); 251 | let temp = &DensePolynomial::from_coefficients_slice(&[z_Vn.evaluate(&beta)]) * &poly_h_1; 252 | poly_p = &poly_p - &temp; 253 | 254 | //////////////////////////// 255 | // p(Y) = p(Y) - z_Vm(alpha) h_2(alpha, Y) 256 | 257 | // poly_h_2_alpha = h_2(alpha, Y) 258 | let mut poly_h_2_alpha = DensePolynomial::from_coefficients_slice(&[E::Fr::zero()]); 259 | for (s, H_s_poly) in vec_H_s_polys.iter().enumerate() { 260 | let h_s_j = DensePolynomial::from_coefficients_slice(&[H_s_poly.evaluate(&alpha)]); 261 | poly_h_2_alpha = &poly_h_2_alpha + &(&h_s_j * &pp.lagrange_polynomials_n[s]); 262 | } 263 | 264 | let temp = 265 | &DensePolynomial::from_coefficients_slice(&[z_Vm.evaluate(&alpha)]) * &poly_h_2_alpha; 266 | poly_p = &poly_p - &temp; 267 | 268 | // check p(beta) = 0 269 | assert!(poly_p.evaluate(&beta) == E::Fr::zero()); 270 | end_timer!(step10_timer); 271 | ////////////////////////////////////////////////////////////////////////////////////////////////////////// 272 | // 11. Open KZG commitments 273 | ////////////////////////////////////////////////////////////////////////////////////////////////////////// 274 | let step11_timer = start_timer!(|| "step 11"); 275 | // KZG.Open( srs, u(X), deg = bot, X = alpha ) 276 | let (evals_1, pi_1) = KZGCommit::open_g1_batch(&pp.poly_ck, &vec_u_polys[0], None, &[alpha]); 277 | 278 | // KZG.Open( srs, U_bar(X,Y), deg = bot, X = alpha ) 279 | let (g1_u_bar_alpha, pi_2, poly_u_bar_alpha) = 280 | KZGCommit::partial_open_g1(pp, &bipoly_U_bar, pp.domain_n.size(), &alpha); 281 | 282 | // KZG.Open( srs, h_2(X,Y), deg = bot, X = alpha ) 283 | let (g1_h_2_alpha, pi_3, _) = 284 | KZGCommit::partial_open_g1(pp, &bipoly_h_2, pp.domain_n.size(), &alpha); 285 | 286 | // KZG.Open( srs, U_bar(alpha,Y), deg = bot, Y = [1, beta, beta * sigma] ) 287 | // should evaluate to (0, v2, v3) 288 | let (evals_2, pi_4) = KZGCommit::open_g1_batch( 289 | &pp.poly_ck, 290 | &poly_u_bar_alpha, 291 | Some(&(pp.domain_n.size() - 1)), 292 | &[E::Fr::one(), beta, beta * pp.domain_n.element(1)], 293 | ); 294 | assert!(evals_2[0] == E::Fr::zero()); 295 | 296 | // KZG.Open(srs, p(Y), deg = n-1, Y = beta) 297 | let (evals_3, pi_5) = KZGCommit::open_g1_batch( 298 | &pp.poly_ck, 299 | &poly_p, 300 | Some(&(pp.domain_n.size() - 1)), 301 | &[beta], 302 | ); 303 | assert!(evals_3[0] == E::Fr::zero()); 304 | end_timer!(step11_timer); 305 | end_timer!(timer); 306 | ProofMultiUnity { 307 | g1_u_bar, 308 | g1_h_1, 309 | g1_h_2, 310 | g1_u_bar_alpha, 311 | g1_h_2_alpha, 312 | v1: evals_1[0], 313 | v2: evals_2[1], 314 | v3: evals_2[2], 315 | pi_1, 316 | pi_2, 317 | pi_3, 318 | pi_4, 319 | pi_5, 320 | } 321 | } 322 | 323 | // Verify that the prover knows vec_u_evals such that g1_u = g1^(sum_j u_j 324 | // mu_j(x)) and u_j^N = 1 325 | #[allow(non_snake_case)] 326 | pub fn verify_multiunity( 327 | pp: &PublicParameters, 328 | transcript: &mut CaulkTranscript, 329 | g1_u: &E::G1Affine, 330 | pi_unity: &ProofMultiUnity, 331 | rng: &mut R, 332 | ) -> bool { 333 | let timer = start_timer!(|| "verify multiunity"); 334 | let mut pairing_inputs = verify_multiunity_defer_pairing(pp, transcript, g1_u, pi_unity); 335 | assert_eq!(pairing_inputs.len(), 10); 336 | 337 | let pairing_timer = start_timer!(|| "pairing product"); 338 | let mut zeta = E::Fr::rand(rng); 339 | pairing_inputs[2].0.mul_assign(zeta); 340 | pairing_inputs[3].0.mul_assign(zeta); 341 | zeta.square_in_place(); 342 | pairing_inputs[4].0.mul_assign(zeta); 343 | pairing_inputs[5].0.mul_assign(zeta); 344 | zeta.square_in_place(); 345 | pairing_inputs[6].0.mul_assign(zeta); 346 | pairing_inputs[7].0.mul_assign(zeta); 347 | zeta.square_in_place(); 348 | pairing_inputs[8].0.mul_assign(zeta); 349 | pairing_inputs[9].0.mul_assign(zeta); 350 | 351 | let prepared_pairing_inputs: Vec<(E::G1Prepared, E::G2Prepared)> = pairing_inputs 352 | .iter() 353 | .map(|(g1, g2)| { 354 | ( 355 | E::G1Prepared::from(g1.into_affine()), 356 | E::G2Prepared::from(g2.into_affine()), 357 | ) 358 | }) 359 | .collect(); 360 | let res = E::product_of_pairings(prepared_pairing_inputs.iter()).is_one(); 361 | 362 | end_timer!(pairing_timer); 363 | end_timer!(timer); 364 | res 365 | } 366 | 367 | // Verify that the prover knows vec_u_evals such that g1_u = g1^(sum_j u_j 368 | // mu_j(x)) and u_j^N = 1 369 | #[allow(non_snake_case)] 370 | pub fn verify_multiunity_defer_pairing( 371 | pp: &PublicParameters, 372 | transcript: &mut CaulkTranscript, 373 | g1_u: &E::G1Affine, 374 | pi_unity: &ProofMultiUnity, 375 | ) -> Vec<(E::G1Projective, E::G2Projective)> { 376 | let timer = start_timer!(|| "verify multiunity (deferring pairing)"); 377 | //////////////////////////// 378 | // alpha = Hash(g1_u, g1_u_bar, g1_h_2) 379 | //////////////////////////// 380 | transcript.append_element(b"u", g1_u); 381 | transcript.append_element(b"u_bar", &pi_unity.g1_u_bar); 382 | transcript.append_element(b"h2", &pi_unity.g1_h_2); 383 | let alpha = transcript.get_and_append_challenge(b"alpha"); 384 | 385 | //////////////////////////// 386 | // beta = Hash( g1_h_1 ) 387 | //////////////////////////// 388 | transcript.append_element(b"h1", &pi_unity.g1_h_1); 389 | let beta = transcript.get_and_append_challenge(b"beta"); 390 | 391 | ///////////////////////////// 392 | // Compute [P]_1 393 | //////////////////////////// 394 | 395 | let u_alpha_beta = pi_unity.v1 * pp.lagrange_polynomials_n[0].evaluate(&beta) + pi_unity.v2; 396 | 397 | // g1_P = [ U^2 - (v3 + id(alpha)* pn(beta) )]_1 398 | let mut g1_P = pp.poly_ck.powers_of_g[0].mul( 399 | u_alpha_beta * u_alpha_beta 400 | - (pi_unity.v3 401 | + (pp.id_poly.evaluate(&alpha) 402 | * pp.lagrange_polynomials_n[pp.n - 1].evaluate(&beta))), 403 | ); 404 | 405 | // g1_P = g1_P - h1 zVn(beta) 406 | let zVn = pp.domain_n.vanishing_polynomial(); 407 | g1_P -= pi_unity.g1_h_1.mul(zVn.evaluate(&beta)); 408 | 409 | // g1_P = g1_P - h2_alpha zVm(alpha) 410 | let zVm = pp.domain_m.vanishing_polynomial(); 411 | g1_P -= pi_unity.g1_h_2_alpha.mul(zVm.evaluate(&alpha)); 412 | 413 | ///////////////////////////// 414 | // Check the KZG openings 415 | //////////////////////////// 416 | 417 | let check1 = KZGCommit::::verify_g1_defer_pairing( 418 | &pp.poly_ck.powers_of_g, 419 | &pp.g2_powers, 420 | g1_u, 421 | None, 422 | &[alpha], 423 | &[pi_unity.v1], 424 | &pi_unity.pi_1, 425 | ); 426 | let check2 = KZGCommit::partial_verify_g1_defer_pairing( 427 | pp, 428 | &pi_unity.g1_u_bar, 429 | pp.domain_n.size(), 430 | &alpha, 431 | &pi_unity.g1_u_bar_alpha, 432 | &pi_unity.pi_2, 433 | ); 434 | let check3 = KZGCommit::partial_verify_g1_defer_pairing( 435 | pp, 436 | &pi_unity.g1_h_2, 437 | pp.domain_n.size(), 438 | &alpha, 439 | &pi_unity.g1_h_2_alpha, 440 | &pi_unity.pi_3, 441 | ); 442 | let check4 = KZGCommit::::verify_g1_defer_pairing( 443 | &pp.poly_ck.powers_of_g, 444 | &pp.g2_powers, 445 | &pi_unity.g1_u_bar_alpha, 446 | Some(&(pp.domain_n.size() - 1)), 447 | &[E::Fr::one(), beta, beta * pp.domain_n.element(1)], 448 | &[E::Fr::zero(), pi_unity.v2, pi_unity.v3], 449 | &pi_unity.pi_4, 450 | ); 451 | let check5 = KZGCommit::::verify_g1_defer_pairing( 452 | &pp.poly_ck.powers_of_g, 453 | &pp.g2_powers, 454 | &g1_P.into_affine(), 455 | Some(&(pp.domain_n.size() - 1)), 456 | &[beta], 457 | &[E::Fr::zero()], 458 | &pi_unity.pi_5, 459 | ); 460 | 461 | let res = [ 462 | check1.as_slice(), 463 | check2.as_slice(), 464 | check3.as_slice(), 465 | check4.as_slice(), 466 | check5.as_slice(), 467 | ] 468 | .concat(); 469 | end_timer!(timer); 470 | res 471 | } 472 | 473 | #[cfg(test)] 474 | pub mod tests { 475 | use super::{prove_multiunity, verify_multiunity}; 476 | use crate::{util::convert_to_bigints, CaulkTranscript}; 477 | use ark_bls12_377::Bls12_377; 478 | use ark_bls12_381::Bls12_381; 479 | use ark_ec::{msm::VariableBaseMSM, PairingEngine, ProjectiveCurve}; 480 | use ark_poly::{ 481 | univariate::DensePolynomial, EvaluationDomain, Evaluations as EvaluationsOnDomain, 482 | UVPolynomial, 483 | }; 484 | use ark_std::test_rng; 485 | use rand::Rng; 486 | use std::time::Instant; 487 | 488 | #[test] 489 | fn test_unity() { 490 | test_unity_helper::(); 491 | test_unity_helper::(); 492 | } 493 | 494 | #[allow(non_snake_case)] 495 | fn test_unity_helper() { 496 | let mut rng = test_rng(); 497 | 498 | let n: usize = 8; // bitlength of poly degree 499 | let max_degree: usize = (1 << n) + 2; 500 | let N: usize = (1 << n) - 1; 501 | 502 | let m_bitsize: usize = 3; 503 | let m: usize = (1 << m_bitsize) - 1; 504 | 505 | // run the setup 506 | let now = Instant::now(); 507 | let pp = crate::multi::PublicParameters::::setup(&max_degree, &N, &m, &n); 508 | println!( 509 | "time to setup single openings of table size {:?} = {:?}", 510 | N + 1, 511 | now.elapsed() 512 | ); 513 | 514 | //////////////////////////////////////////////////////////////////////////////////// 515 | // generating values for testing 516 | //////////////////////////////////////////////////////////////////////////////////// 517 | 518 | // choose [u1, ..., um] such that uj**N = 1 519 | let mut vec_u_evals: Vec = Vec::new(); 520 | for _ in 0..m { 521 | let j = rng.gen_range(0..pp.domain_N.size()); 522 | vec_u_evals.push(pp.domain_N.element(j)); 523 | } 524 | 525 | // choose random quotient polynomial of degree 1. 526 | let u_poly_quotient = DensePolynomial::::rand(5, &mut rng); 527 | 528 | // X^m - 1 529 | let z_Vm: DensePolynomial = pp.domain_m.vanishing_polynomial().into(); 530 | 531 | // commit to polynomial u(X) = sum_j uj muj(X) + u_quotient(X) z_Vm(X) 532 | let u_poly = &EvaluationsOnDomain::from_vec_and_domain(vec_u_evals.clone(), pp.domain_m) 533 | .interpolate() 534 | + &(&u_poly_quotient * &z_Vm); 535 | 536 | assert!(pp.poly_ck.powers_of_g.len() >= u_poly.len()); 537 | let g1_u = VariableBaseMSM::multi_scalar_mul( 538 | &pp.poly_ck.powers_of_g, 539 | convert_to_bigints(&u_poly.coeffs).as_slice(), 540 | ) 541 | .into_affine(); 542 | 543 | //////////////////////////////////////////////////////////////////////////////////// 544 | // run the prover 545 | //////////////////////////////////////////////////////////////////////////////////// 546 | let mut prover_transcript = CaulkTranscript::new(); 547 | let pi_unity = prove_multiunity::( 548 | &pp, 549 | &mut prover_transcript, 550 | &g1_u, 551 | &vec_u_evals, 552 | u_poly_quotient, 553 | ); 554 | 555 | //////////////////////////////////////////////////////////////////////////////////// 556 | // run the verifier 557 | //////////////////////////////////////////////////////////////////////////////////// 558 | let mut verifier_transcript = CaulkTranscript::new(); 559 | println!( 560 | "unity proof verifies {:?}", 561 | verify_multiunity::(&pp, &mut verifier_transcript, &g1_u, &pi_unity, &mut rng) 562 | ); 563 | } 564 | } 565 | -------------------------------------------------------------------------------- /src/pedersen.rs: -------------------------------------------------------------------------------- 1 | // This file includes a prover and verifier for demonstrating knowledge of an 2 | // opening of a Pedersen commitment. The protocol is informally described in 3 | // Appendix A.2, Proof of Opening of a Pedersen Commitment 4 | 5 | use crate::CaulkTranscript; 6 | use ark_ec::{AffineCurve, ProjectiveCurve}; 7 | use ark_ff::PrimeField; 8 | use ark_std::{end_timer, rand::RngCore, start_timer, UniformRand}; 9 | use std::marker::PhantomData; 10 | 11 | // Parameters for pedersen commitment 12 | pub struct PedersenParam { 13 | pub g: C, 14 | pub h: C, 15 | } 16 | 17 | // Structure of proof output by prove_pedersen 18 | pub struct PedersenProof { 19 | pub g1_r: C, 20 | pub t1: C::ScalarField, 21 | pub t2: C::ScalarField, 22 | } 23 | 24 | pub struct PedersenCommit { 25 | phantom: PhantomData, 26 | } 27 | 28 | impl PedersenCommit { 29 | // prove knowledge of a and b such that cm = g^a h^b 30 | pub fn prove( 31 | param: &PedersenParam, 32 | transcript: &mut CaulkTranscript, 33 | cm: &C, 34 | a: &C::ScalarField, 35 | b: &C::ScalarField, 36 | rng: &mut R, 37 | ) -> PedersenProof { 38 | let timer = start_timer!(|| "prove pedersen commit"); 39 | // R = g^s1 h^s2 40 | let s1 = C::ScalarField::rand(rng); 41 | let s2 = C::ScalarField::rand(rng); 42 | 43 | let g1_r = (param.g.mul(s1) + param.h.mul(s2.into_repr())).into_affine(); 44 | 45 | // c = Hash(cm, R) 46 | transcript.append_element(b"commitment", cm); 47 | transcript.append_element(b"g1_r", &g1_r); 48 | let c = transcript.get_and_append_challenge(b"get c"); 49 | 50 | let t1 = s1 + c * a; 51 | let t2 = s2 + c * b; 52 | end_timer!(timer); 53 | PedersenProof { g1_r, t1, t2 } 54 | } 55 | 56 | // Verify that prover knows a and b such that cm = g^a h^b 57 | pub fn verify( 58 | param: &PedersenParam, 59 | transcript: &mut CaulkTranscript, 60 | cm: &C, 61 | proof: &PedersenProof, 62 | ) -> bool { 63 | let timer = start_timer!(|| "verify pedersen commit"); 64 | // compute c = Hash(cm, R) 65 | transcript.append_element(b"commitment", cm); 66 | transcript.append_element(b"g1_r", &proof.g1_r); 67 | let c = transcript.get_and_append_challenge(b"get c"); 68 | 69 | // check that R g^(-t1) h^(-t2) cm^(c) = 1 70 | let res = proof.g1_r.into_projective() + cm.mul(c) 71 | == param.g.mul(proof.t1) + param.h.mul(proof.t2); 72 | end_timer!(timer); 73 | res 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/single/mod.rs: -------------------------------------------------------------------------------- 1 | // This file includes the Caulk prover and verifier for single openings. 2 | // The protocol is described in Figure 1. 3 | 4 | pub mod setup; 5 | pub mod unity; 6 | 7 | use crate::{ 8 | pedersen::{PedersenCommit, PedersenProof}, 9 | CaulkTranscript, 10 | }; 11 | use ark_ec::{AffineCurve, PairingEngine, ProjectiveCurve}; 12 | use ark_ff::{Field, PrimeField}; 13 | use ark_poly::{EvaluationDomain, GeneralEvaluationDomain}; 14 | use ark_std::{end_timer, rand::RngCore, start_timer, One, UniformRand, Zero}; 15 | use setup::{PublicParameters, VerifierPublicParameters}; 16 | use std::ops::Neg; 17 | use unity::{ 18 | caulk_single_unity_prove, caulk_single_unity_verify, CaulkProofUnity, PublicParametersUnity, 19 | VerifierPublicParametersUnity, 20 | }; 21 | 22 | // Structure of opening proofs output by prove. 23 | #[allow(non_snake_case)] 24 | pub struct CaulkProof { 25 | pub g2_z: E::G2Affine, 26 | pub g1_T: E::G1Affine, 27 | pub g2_S: E::G2Affine, 28 | pub pi_ped: PedersenProof, 29 | pub pi_unity: CaulkProofUnity, 30 | } 31 | 32 | // Proves knowledge of (i, Q, z, r) such that 33 | // 1) Q is a KZG opening proof that g1_C opens to z at i 34 | // 2) cm = g^z h^r 35 | 36 | // Takes as input opening proof Q. Does not need knowledge of contents of C = 37 | // g1_C. 38 | #[allow(non_snake_case)] 39 | #[allow(clippy::too_many_arguments)] 40 | pub fn caulk_single_prove( 41 | pp: &PublicParameters, 42 | transcript: &mut CaulkTranscript, 43 | g1_C: &E::G1Affine, 44 | cm: &E::G1Affine, 45 | index: usize, 46 | g1_q: &E::G1Affine, 47 | v: &E::Fr, 48 | r: &E::Fr, 49 | rng: &mut R, 50 | ) -> CaulkProof { 51 | let timer = start_timer!(|| "single proof"); 52 | // provers blinders for zero-knowledge 53 | let a = E::Fr::rand(rng); 54 | let s = E::Fr::rand(rng); 55 | 56 | let domain_H: GeneralEvaluationDomain = 57 | GeneralEvaluationDomain::new(pp.verifier_pp.domain_H_size).unwrap(); 58 | 59 | /////////////////////////////// 60 | // Compute [z]_2, [T]_1, and [S]_2 61 | /////////////////////////////// 62 | 63 | // [z]_2 = [ a (x - omega^i) ]_2 64 | let g2_z = (pp.verifier_pp.poly_vk.beta_h.mul(a) 65 | + pp.verifier_pp.poly_vk.h.mul(-a * domain_H.element(index))) 66 | .into_affine(); 67 | 68 | // [T]_1 = [ ( a^(-1) Q + s h]_1 for Q precomputed KZG opening. 69 | let g1_T = 70 | (g1_q.mul(a.inverse().unwrap()) + pp.verifier_pp.pedersen_param.h.mul(s)).into_affine(); 71 | 72 | // [S]_2 = [ - r - s z ]_2 73 | let g2_S = (pp.verifier_pp.poly_vk.h.mul((-*r).into_repr()) + g2_z.mul((-s).into_repr())) 74 | .into_affine(); 75 | 76 | /////////////////////////////// 77 | // Pedersen prove 78 | /////////////////////////////// 79 | 80 | // hash the instance and the proof elements to determine hash inputs for 81 | // Pedersen prover 82 | 83 | transcript.append_element(b"0", &E::Fr::zero()); 84 | transcript.append_element(b"C", g1_C); 85 | transcript.append_element(b"T", &g1_T); 86 | transcript.append_element(b"z", &g2_z); 87 | transcript.append_element(b"S", &g2_S); 88 | 89 | // proof that cm = g^z h^rs 90 | let pi_ped = PedersenCommit::prove(&pp.verifier_pp.pedersen_param, transcript, cm, v, r, rng); 91 | 92 | /////////////////////////////// 93 | // Unity prove 94 | /////////////////////////////// 95 | 96 | // hash the last round of the pedersen proof to determine hash input to the 97 | // unity prover 98 | transcript.append_element(b"t1", &pi_ped.t1); 99 | transcript.append_element(b"t2", &pi_ped.t2); 100 | 101 | // Setting up the public parameters for the unity prover 102 | let pp_unity = PublicParametersUnity::from(pp); 103 | 104 | // proof that A = [a x - b ]_2 for a^n = b^n 105 | let pi_unity = caulk_single_unity_prove( 106 | &pp_unity, 107 | transcript, 108 | &g2_z, 109 | &a, 110 | &(a * domain_H.element(index)), 111 | rng, 112 | ); 113 | 114 | end_timer!(timer); 115 | CaulkProof { 116 | g2_z, 117 | g1_T, 118 | g2_S, 119 | pi_ped, 120 | pi_unity, 121 | } 122 | } 123 | 124 | // Verifies that the prover knows of (i, Q, z, r) such that 125 | // 1) Q is a KZG opening proof that g1_C opens to z at i 126 | // 2) cm = g^z h^r 127 | #[allow(non_snake_case)] 128 | pub fn caulk_single_verify( 129 | vk: &VerifierPublicParameters, 130 | transcript: &mut CaulkTranscript, 131 | g1_C: &E::G1Affine, 132 | cm: &E::G1Affine, 133 | proof: &CaulkProof, 134 | ) -> bool { 135 | let timer = start_timer!(|| "single verify"); 136 | /////////////////////////////// 137 | // Pairing check 138 | /////////////////////////////// 139 | 140 | // check that e( - C + cm, [1]_2) + e( [T]_1, [z]_2 ) + e( [h]_1, [S]_2 ) = 1 141 | let eq1: Vec<(E::G1Prepared, E::G2Prepared)> = vec![ 142 | ((g1_C.neg() + *cm).into(), vk.poly_vk.prepared_h.clone()), 143 | ((proof.g1_T).into(), proof.g2_z.into()), 144 | (vk.pedersen_param.h.into(), proof.g2_S.into()), 145 | ]; 146 | 147 | let check1 = E::product_of_pairings(&eq1).is_one(); 148 | 149 | /////////////////////////////// 150 | // Pedersen check 151 | /////////////////////////////// 152 | 153 | // hash the instance and the proof elements to determine hash inputs for 154 | // Pedersen prover 155 | transcript.append_element(b"0", &E::Fr::zero()); 156 | transcript.append_element(b"C", g1_C); 157 | transcript.append_element(b"T", &proof.g1_T); 158 | transcript.append_element(b"z", &proof.g2_z); 159 | transcript.append_element(b"S", &proof.g2_S); 160 | 161 | // verify that cm = [v + r h] 162 | let check2 = PedersenCommit::verify(&vk.pedersen_param, transcript, cm, &proof.pi_ped); 163 | 164 | /////////////////////////////// 165 | // Unity check 166 | /////////////////////////////// 167 | 168 | // hash the last round of the pedersen proof to determine hash input to the 169 | // unity prover 170 | transcript.append_element(b"t1", &proof.pi_ped.t1); 171 | transcript.append_element(b"t2", &proof.pi_ped.t2); 172 | 173 | let vk_unity = VerifierPublicParametersUnity::from(vk); 174 | 175 | // Verify that g2_z = [ ax - b ]_1 for (a/b)**N = 1 176 | let check3 = caulk_single_unity_verify(&vk_unity, transcript, &proof.g2_z, &proof.pi_unity); 177 | 178 | end_timer!(timer); 179 | check1 && check2 && check3 180 | } 181 | 182 | #[cfg(test)] 183 | mod tests { 184 | 185 | use crate::{ 186 | caulk_single_prove, caulk_single_setup, caulk_single_verify, CaulkTranscript, KZGCommit, 187 | }; 188 | use ark_bls12_381::{Bls12_381, Fr, G1Affine}; 189 | use ark_ec::{AffineCurve, ProjectiveCurve}; 190 | use ark_poly::{ 191 | univariate::DensePolynomial, EvaluationDomain, GeneralEvaluationDomain, Polynomial, 192 | UVPolynomial, 193 | }; 194 | use ark_poly_commit::kzg10::KZG10; 195 | use ark_std::{test_rng, UniformRand}; 196 | 197 | type UniPoly381 = DensePolynomial; 198 | type KzgBls12_381 = KZG10; 199 | 200 | #[test] 201 | #[allow(non_snake_case)] 202 | fn test_caulk_single_end_to_end() { 203 | for p in 4..7 { 204 | let mut rng = test_rng(); 205 | // setting public parameters 206 | // current kzg setup should be changed with output from a setup ceremony 207 | let max_degree: usize = (1 << p) + 2; 208 | let actual_degree: usize = (1 << p) - 1; 209 | 210 | // run the setup 211 | let pp = caulk_single_setup(max_degree, actual_degree, &mut rng); 212 | 213 | // polynomial and commitment 214 | // deterministic randomness. Should never be used in practice. 215 | let c_poly = UniPoly381::rand(actual_degree, &mut rng); 216 | let (g1_C, _) = KzgBls12_381::commit(&pp.poly_ck, &c_poly, None, None).unwrap(); 217 | let g1_C = g1_C.0; 218 | 219 | // point at which we will open c_com 220 | let input_domain: GeneralEvaluationDomain = 221 | EvaluationDomain::new(actual_degree).unwrap(); 222 | 223 | let position = 1; 224 | let omega_i = input_domain.element(position); 225 | 226 | // z = c(w_i) and cm = g^z h^r for random r 227 | let z = c_poly.evaluate(&omega_i); 228 | let r = Fr::rand(&mut rng); 229 | let cm = (pp.verifier_pp.pedersen_param.g.mul(z) 230 | + pp.verifier_pp.pedersen_param.h.mul(r)) 231 | .into_affine(); 232 | 233 | let mut prover_transcript = CaulkTranscript::::new(); 234 | let mut verifier_transcript = CaulkTranscript::::new(); 235 | 236 | // open single position at 0 237 | { 238 | let a = KZGCommit::open_g1_batch(&pp.poly_ck, &c_poly, None, &[omega_i]); 239 | let g1_q = a.1; 240 | 241 | // run the prover 242 | let proof_evaluate = caulk_single_prove( 243 | &pp, 244 | &mut prover_transcript, 245 | &g1_C, 246 | &cm, 247 | position, 248 | &g1_q, 249 | &z, 250 | &r, 251 | &mut rng, 252 | ); 253 | 254 | // run the verifier 255 | assert!(caulk_single_verify( 256 | &pp.verifier_pp, 257 | &mut verifier_transcript, 258 | &g1_C, 259 | &cm, 260 | &proof_evaluate, 261 | )); 262 | } 263 | // compute all openings 264 | { 265 | let g1_qs = KZGCommit::::multiple_open::( 266 | &c_poly, 267 | &pp.poly_ck.powers_of_g, 268 | p, 269 | ); 270 | let g1_q = g1_qs[position]; 271 | 272 | // run the prover 273 | let proof_evaluate = caulk_single_prove( 274 | &pp, 275 | &mut prover_transcript, 276 | &g1_C, 277 | &cm, 278 | position, 279 | &g1_q, 280 | &z, 281 | &r, 282 | &mut rng, 283 | ); 284 | // run the verifier 285 | assert!(caulk_single_verify( 286 | &pp.verifier_pp, 287 | &mut verifier_transcript, 288 | &g1_C, 289 | &cm, 290 | &proof_evaluate, 291 | )); 292 | } 293 | } 294 | } 295 | } 296 | -------------------------------------------------------------------------------- /src/single/setup.rs: -------------------------------------------------------------------------------- 1 | // This file includes the setup algorithm for Caulk with single openings. 2 | // A full description of the setup is not formally given in the paper. 3 | 4 | use crate::{util::trim, PedersenParam}; 5 | use ark_ec::{AffineCurve, PairingEngine, ProjectiveCurve}; 6 | use ark_ff::{Field, UniformRand}; 7 | use ark_poly::{ 8 | univariate::DensePolynomial, EvaluationDomain, Evaluations as EvaluationsOnDomain, 9 | GeneralEvaluationDomain, UVPolynomial, 10 | }; 11 | use ark_poly_commit::kzg10::*; 12 | use ark_std::{cfg_into_iter, end_timer, rand::RngCore, start_timer, One, Zero}; 13 | #[cfg(feature = "parallel")] 14 | use rayon::iter::{IntoParallelIterator, ParallelIterator}; 15 | use std::cmp::max; 16 | 17 | // structure of public parameters 18 | #[allow(non_snake_case)] 19 | pub struct PublicParameters { 20 | pub poly_ck: Powers<'static, E>, 21 | pub poly_ck_d: E::G1Affine, 22 | pub lagrange_polynomials_Vn: Vec>, 23 | pub verifier_pp: VerifierPublicParameters, 24 | } 25 | 26 | // smaller set of public parameters used by verifier 27 | #[allow(non_snake_case)] 28 | pub struct VerifierPublicParameters { 29 | pub poly_ck_pen: E::G1Affine, 30 | pub lagrange_scalars_Vn: Vec, 31 | pub poly_prod: DensePolynomial, 32 | pub poly_vk: VerifierKey, 33 | pub pedersen_param: PedersenParam, 34 | pub g1_x: E::G1Affine, 35 | pub domain_H_size: usize, 36 | pub logN: usize, 37 | pub domain_Vn: GeneralEvaluationDomain, 38 | pub powers_of_g2: Vec, 39 | } 40 | 41 | // setup algorithm for Caulk with single openings 42 | // also includes a bunch of precomputation. 43 | #[allow(non_snake_case)] 44 | pub fn caulk_single_setup( 45 | max_degree: usize, 46 | actual_degree: usize, 47 | rng: &mut R, 48 | ) -> PublicParameters { 49 | let total_time = start_timer!(|| "total srs setup"); 50 | 51 | // domain where vector commitment is defined 52 | let domain_H: GeneralEvaluationDomain = 53 | GeneralEvaluationDomain::new(actual_degree).unwrap(); 54 | 55 | let logN: usize = ((actual_degree as f32).log(2.0)).ceil() as usize; 56 | 57 | // smaller domain for unity proofs with generator w 58 | let domain_Vn: GeneralEvaluationDomain = GeneralEvaluationDomain::new(6 + logN).unwrap(); 59 | 60 | // Determining how big an srs we need. 61 | // Need an srs of size actual_degree to commit to the polynomial. 62 | // Need an srs of size 2 * domain_Vn_size + 3 to run the unity prover. 63 | // We take the larger of the two. 64 | let poly_ck_size = max(actual_degree, 2 * domain_Vn.size() + 3); 65 | 66 | // Setup algorithm. To be replaced by output of a universal setup before being 67 | // production ready. 68 | let powers_time = start_timer!(|| "setup powers"); 69 | let srs = KZG10::>::setup(max(max_degree, poly_ck_size), true, rng) 70 | .unwrap(); 71 | end_timer!(powers_time); 72 | 73 | // trim down to size. 74 | let (poly_ck, poly_vk) = trim::>(&srs, poly_ck_size); 75 | 76 | // g^x^d = maximum power given in setup 77 | let poly_ck_d = srs.powers_of_g[srs.powers_of_g.len() - 1]; 78 | 79 | // g^x^(d-1) = penultimate power given in setup 80 | let poly_ck_pen = srs.powers_of_g[srs.powers_of_g.len() - 2]; 81 | 82 | // random pedersen commitment generatoor 83 | let ped_h: E::G1Affine = E::G1Projective::rand(rng).into_affine(); 84 | 85 | // precomputation to speed up prover 86 | // lagrange_polynomials_Vn[i] = polynomial equal to 0 at w^j for j!= i and 1 at 87 | // w^i 88 | let mut lagrange_polynomials_Vn: Vec> = Vec::new(); 89 | 90 | // precomputation to speed up verifier. 91 | // scalars such that lagrange_scalars_Vn[i] = prod_(j != i) (w^i - w^j)^(-1) 92 | let mut lagrange_scalars_Vn: Vec = Vec::new(); 93 | 94 | for i in 0..domain_Vn.size() { 95 | let evals: Vec = cfg_into_iter!(0..domain_Vn.size()) 96 | .map(|k| if k == i { E::Fr::one() } else { E::Fr::zero() }) 97 | .collect(); 98 | lagrange_polynomials_Vn 99 | .push(EvaluationsOnDomain::from_vec_and_domain(evals, domain_Vn).interpolate()); 100 | } 101 | 102 | for i in 0..5 { 103 | let mut temp = E::Fr::one(); 104 | for j in 0..domain_Vn.size() { 105 | if j != i { 106 | temp *= domain_Vn.element(i) - domain_Vn.element(j); 107 | } 108 | } 109 | lagrange_scalars_Vn.push(temp.inverse().unwrap()); 110 | } 111 | 112 | // also want lagrange_scalars_Vn[logN + 5] 113 | let mut temp = E::Fr::one(); 114 | for j in 0..domain_Vn.size() { 115 | if j != (logN + 5) { 116 | temp *= domain_Vn.element(logN + 5) - domain_Vn.element(j); 117 | } 118 | } 119 | lagrange_scalars_Vn.push(temp.inverse().unwrap()); 120 | 121 | // poly_prod = (X - 1) (X - w) (X - w^2) (X - w^3) (X - w^4) (X - w^(5 + logN)) 122 | // (X - w^(6 + logN)) for efficiency not including (X - w^i) for i > 6 + 123 | // logN prover sets these evaluations to 0 anyway. 124 | let mut poly_prod = DensePolynomial::from_coefficients_slice(&[E::Fr::one()]); 125 | for i in 0..domain_Vn.size() { 126 | if i < 5 { 127 | poly_prod = &poly_prod 128 | * (&DensePolynomial::from_coefficients_slice(&[ 129 | -domain_Vn.element(i), 130 | E::Fr::one(), 131 | ])) 132 | } 133 | if i == (5 + logN) { 134 | poly_prod = &poly_prod 135 | * (&DensePolynomial::from_coefficients_slice(&[ 136 | -domain_Vn.element(i), 137 | E::Fr::one(), 138 | ])) 139 | } 140 | if i == (6 + logN) { 141 | poly_prod = &poly_prod 142 | * (&DensePolynomial::from_coefficients_slice(&[ 143 | -domain_Vn.element(i), 144 | E::Fr::one(), 145 | ])) 146 | } 147 | } 148 | 149 | // ped_g = g^x^0 from kzg commitment key. 150 | let ped_g = poly_ck.powers_of_g[0]; 151 | 152 | // need some powers of g2 153 | // arkworks setup doesn't give these powers but the setup does use a fixed 154 | // randomness to generate them. so we can generate powers of g2 directly. 155 | let rng = &mut ark_std::test_rng(); 156 | let beta = E::Fr::rand(rng); 157 | let mut temp = poly_vk.h; 158 | 159 | let mut powers_of_g2: Vec = Vec::new(); 160 | for _ in 0..3 { 161 | powers_of_g2.push(temp); 162 | temp = temp.mul(beta).into_affine(); 163 | } 164 | 165 | let verifier_pp = VerifierPublicParameters { 166 | poly_ck_pen, 167 | lagrange_scalars_Vn, 168 | poly_prod, 169 | poly_vk, 170 | pedersen_param: PedersenParam { g: ped_g, h: ped_h }, 171 | g1_x: srs.powers_of_g[1], 172 | domain_H_size: domain_H.size(), 173 | logN, 174 | domain_Vn, 175 | powers_of_g2, 176 | }; 177 | 178 | let pp = PublicParameters { 179 | poly_ck, 180 | poly_ck_d, 181 | lagrange_polynomials_Vn, 182 | verifier_pp, 183 | }; 184 | 185 | end_timer!(total_time); 186 | pp 187 | } 188 | -------------------------------------------------------------------------------- /src/single/unity.rs: -------------------------------------------------------------------------------- 1 | // This file includes the Caulk's unity prover and verifier for single openings. 2 | // The protocol is described in Figure 2. 3 | 4 | use super::setup::{PublicParameters, VerifierPublicParameters}; 5 | use crate::{kzg::KZGCommit, CaulkTranscript}; 6 | use ark_ec::{AffineCurve, PairingEngine, ProjectiveCurve}; 7 | use ark_ff::Field; 8 | use ark_poly::{ 9 | univariate::DensePolynomial, EvaluationDomain, Evaluations as EvaluationsOnDomain, 10 | GeneralEvaluationDomain, Polynomial, UVPolynomial, 11 | }; 12 | use ark_poly_commit::kzg10::*; 13 | use ark_std::{cfg_into_iter, end_timer, rand::RngCore, start_timer, One, UniformRand, Zero}; 14 | #[cfg(feature = "parallel")] 15 | use rayon::iter::{IntoParallelIterator, ParallelIterator}; 16 | 17 | // prover public parameters structure for caulk_single_unity_prove 18 | #[allow(non_snake_case)] 19 | pub struct PublicParametersUnity { 20 | pub poly_ck: Powers<'static, E>, 21 | pub gxd: E::G1Affine, 22 | pub gxpen: E::G1Affine, 23 | pub lagrange_polynomials_Vn: Vec>, 24 | pub poly_prod: DensePolynomial, 25 | pub logN: usize, 26 | pub domain_Vn: GeneralEvaluationDomain, 27 | } 28 | 29 | // verifier parameters structure for caulk_single_unity_verify 30 | #[allow(non_snake_case)] 31 | pub struct VerifierPublicParametersUnity { 32 | pub poly_vk: VerifierKey, 33 | pub gxpen: E::G1Affine, 34 | pub g1: E::G1Affine, 35 | pub g1_x: E::G1Affine, 36 | pub lagrange_scalars_Vn: Vec, 37 | pub poly_prod: DensePolynomial, 38 | pub logN: usize, 39 | pub domain_Vn: GeneralEvaluationDomain, 40 | pub powers_of_g2: Vec, 41 | } 42 | 43 | // output structure of caulk_single_unity_prove 44 | #[allow(non_snake_case)] 45 | pub struct CaulkProofUnity { 46 | pub g1_F: E::G1Affine, 47 | pub g1_H: E::G1Affine, 48 | pub v1: E::Fr, 49 | pub v2: E::Fr, 50 | pub pi1: E::G1Affine, 51 | pub pi2: E::G1Affine, 52 | } 53 | 54 | impl From<&PublicParameters> for PublicParametersUnity { 55 | fn from(pp: &PublicParameters) -> Self { 56 | Self { 57 | poly_ck: pp.poly_ck.clone(), 58 | gxd: pp.poly_ck_d, 59 | gxpen: pp.verifier_pp.poly_ck_pen, 60 | lagrange_polynomials_Vn: pp.lagrange_polynomials_Vn.clone(), 61 | poly_prod: pp.verifier_pp.poly_prod.clone(), 62 | logN: pp.verifier_pp.logN, 63 | domain_Vn: pp.verifier_pp.domain_Vn, 64 | } 65 | } 66 | } 67 | 68 | impl From<&VerifierPublicParameters> for VerifierPublicParametersUnity { 69 | fn from(vk: &VerifierPublicParameters) -> Self { 70 | Self { 71 | poly_vk: vk.poly_vk.clone(), 72 | gxpen: vk.poly_ck_pen, 73 | g1: vk.pedersen_param.g, 74 | g1_x: vk.g1_x, 75 | lagrange_scalars_Vn: vk.lagrange_scalars_Vn.clone(), 76 | poly_prod: vk.poly_prod.clone(), 77 | logN: vk.logN, 78 | domain_Vn: vk.domain_Vn, 79 | powers_of_g2: vk.powers_of_g2.clone(), 80 | } 81 | } 82 | } 83 | 84 | // Prove knowledge of a, b such that g2_z = [ax - b]_2 and a^n = b^n 85 | #[allow(non_snake_case)] 86 | pub fn caulk_single_unity_prove( 87 | pp: &PublicParametersUnity, 88 | transcript: &mut CaulkTranscript, 89 | g2_z: &E::G2Affine, 90 | a: &E::Fr, 91 | b: &E::Fr, 92 | rng: &mut R, 93 | ) -> CaulkProofUnity { 94 | let timer = start_timer!(|| "single unity prove"); 95 | // a_poly = a X - b 96 | let a_poly = DensePolynomial::from_coefficients_slice(&[-*b, *a]); 97 | 98 | // provers blinders for zero-knowledge 99 | let r0 = E::Fr::rand(rng); 100 | let r1 = E::Fr::rand(rng); 101 | let r2 = E::Fr::rand(rng); 102 | let r3 = E::Fr::rand(rng); 103 | 104 | let r_poly = DensePolynomial::from_coefficients_slice(&[r1, r2, r3]); 105 | 106 | // roots of unity in domain of size m = log_2(n) + 6 107 | let sigma = pp.domain_Vn.element(1); 108 | 109 | // X^n - 1 110 | let z: DensePolynomial = pp.domain_Vn.vanishing_polynomial().into(); 111 | 112 | // computing [ (a/b), (a/b)^2, (a/b)^4, ..., (a/b)^(2^logN) = (a/b)^n ] 113 | let mut a_div_b = *a * (*b).inverse().unwrap(); 114 | let mut vec_a_div_b: Vec = Vec::new(); 115 | for _ in 0..(pp.logN + 1) { 116 | vec_a_div_b.push(a_div_b); 117 | a_div_b = a_div_b * a_div_b; 118 | } 119 | 120 | //////////////////////////// 121 | // computing f(X). First compute in domain. 122 | //////////////////////////// 123 | let f_evals: Vec = cfg_into_iter!(0..pp.domain_Vn.size()) 124 | .map(|k| { 125 | if k == 0 { 126 | *a - *b 127 | } else if k == 1 { 128 | *a * sigma - *b 129 | } else if k == 2 { 130 | *a 131 | } else if k == 3 { 132 | *b 133 | } else if k > 3 && k < (pp.logN + 5) { 134 | vec_a_div_b[k - 4] 135 | } else if k == pp.logN + 5 { 136 | r0 137 | } else { 138 | E::Fr::zero() 139 | } 140 | }) 141 | .collect(); 142 | 143 | let f_poly = &EvaluationsOnDomain::from_vec_and_domain(f_evals, pp.domain_Vn).interpolate() 144 | + &(&r_poly * &z); 145 | 146 | // computing f( sigma^(-1) X) and f( sigma^(-2) X) 147 | let mut f_poly_shift_1 = f_poly.clone(); 148 | let mut f_poly_shift_2 = f_poly.clone(); 149 | let mut shift_1 = E::Fr::one(); 150 | let mut shift_2 = E::Fr::one(); 151 | 152 | for i in 0..f_poly.len() { 153 | f_poly_shift_1[i] *= shift_1; 154 | f_poly_shift_2[i] *= shift_2; 155 | shift_1 *= pp.domain_Vn.element(pp.domain_Vn.size() - 1); 156 | shift_2 *= pp.domain_Vn.element(pp.domain_Vn.size() - 2); 157 | } 158 | 159 | //////////////////////////// 160 | // computing h(X). First compute p(X) then divide. 161 | //////////////////////////// 162 | 163 | // p(X) = p(X) + (f(X) - a(X)) (rho_1(X) + rho_2(X)) 164 | let mut p_poly = 165 | &(&f_poly - &a_poly) * &(&pp.lagrange_polynomials_Vn[0] + &pp.lagrange_polynomials_Vn[1]); 166 | 167 | // p(X) = p(X) + ( (1 - sigma) f(X) - f(sigma^(-2)X) + f(sigma^(-1) X) ) 168 | // rho_3(X) 169 | p_poly = &p_poly 170 | + &(&(&(&(&DensePolynomial::from_coefficients_slice(&[(E::Fr::one() - sigma)]) 171 | * &f_poly) 172 | - &f_poly_shift_2) 173 | + &f_poly_shift_1) 174 | * &pp.lagrange_polynomials_Vn[2]); 175 | 176 | // p(X) = p(X) + ( -sigma f(sigma^(-1) X) + f(sigma^(-2)X) + f(X) ) rho_4(X) 177 | p_poly = &p_poly 178 | + &(&(&(&(&DensePolynomial::from_coefficients_slice(&[-sigma]) * &f_poly_shift_1) 179 | + &f_poly_shift_2) 180 | + &f_poly) 181 | * &pp.lagrange_polynomials_Vn[3]); 182 | 183 | // p(X) = p(X) + ( f(X) f(sigma^(-1) X) - f(sigma^(-2)X) ) rho_5(X) 184 | p_poly = &p_poly 185 | + &(&(&(&f_poly * &f_poly_shift_1) - &f_poly_shift_2) * &pp.lagrange_polynomials_Vn[4]); 186 | 187 | // p(X) = p(X) + ( f(X) - f(sigma^(-1) X) * f(sigma^(-1)X) ) prod_(i not in 188 | // [5, .. , logN + 4]) (X - sigma^i) 189 | p_poly = &p_poly + &(&(&f_poly - &(&f_poly_shift_1 * &f_poly_shift_1)) * &pp.poly_prod); 190 | 191 | // p(X) = p(X) + ( f(sigma^(-1) X) - 1 ) rho_(logN + 6)(X) 192 | p_poly = &p_poly 193 | + &(&(&f_poly_shift_1 - &(DensePolynomial::from_coefficients_slice(&[E::Fr::one()]))) 194 | * &pp.lagrange_polynomials_Vn[pp.logN + 5]); 195 | 196 | // Compute h_hat_poly = p(X) / z_Vn(X) and abort if division is not perfect 197 | let (h_hat_poly, remainder) = p_poly.divide_by_vanishing_poly(pp.domain_Vn).unwrap(); 198 | assert!(remainder.is_zero(), "z_Vn(X) does not divide p(X)"); 199 | 200 | //////////////////////////// 201 | // Commit to f(X) and h(X) 202 | //////////////////////////// 203 | let g1_F = KZGCommit::::commit_g1(&pp.poly_ck, &f_poly); 204 | let h_hat_com = KZGCommit::::commit_g1(&pp.poly_ck, &h_hat_poly); 205 | 206 | // g1_H is a commitment to h_hat_poly + X^(d-1) z(X) 207 | let g1_H = (h_hat_com.into_projective() + pp.gxd.mul(-*a) + pp.gxpen.mul(*b)).into_affine(); 208 | 209 | //////////////////////////// 210 | // alpha = Hash([z]_2, [F]_1, [H]_1) 211 | //////////////////////////// 212 | transcript.append_element(b"F", &g1_F); 213 | transcript.append_element(b"H", &g1_H); 214 | transcript.append_element(b"z", g2_z); 215 | let alpha = transcript.get_and_append_challenge(b"alpha"); 216 | 217 | //////////////////////////// 218 | // v1 = f(sigma^(-1) alpha) and v2 = f(w^(-2) alpha) 219 | //////////////////////////// 220 | let alpha1 = alpha * pp.domain_Vn.element(pp.domain_Vn.size() - 1); 221 | let alpha2 = alpha * pp.domain_Vn.element(pp.domain_Vn.size() - 2); 222 | let v1 = f_poly.evaluate(&alpha1); 223 | let v2 = f_poly.evaluate(&alpha2); 224 | 225 | //////////////////////////// 226 | // Compute polynomial p_alpha(X) that opens at alpha to 0 227 | //////////////////////////// 228 | 229 | // restating some field elements as polynomials so that can multiply polynomials 230 | let pz_alpha = DensePolynomial::from_coefficients_slice(&[-z.evaluate(&alpha)]); 231 | let pv1 = DensePolynomial::from_coefficients_slice(&[v1]); 232 | let pv2 = DensePolynomial::from_coefficients_slice(&[v2]); 233 | let prho1_add_2 = DensePolynomial::from_coefficients_slice(&[pp.lagrange_polynomials_Vn[0] 234 | .evaluate(&alpha) 235 | + pp.lagrange_polynomials_Vn[1].evaluate(&alpha)]); 236 | let prho3 = 237 | DensePolynomial::from_coefficients_slice(&[pp.lagrange_polynomials_Vn[2].evaluate(&alpha)]); 238 | let prho4 = 239 | DensePolynomial::from_coefficients_slice(&[pp.lagrange_polynomials_Vn[3].evaluate(&alpha)]); 240 | let prho5 = 241 | DensePolynomial::from_coefficients_slice(&[pp.lagrange_polynomials_Vn[4].evaluate(&alpha)]); 242 | let ppolyprod = DensePolynomial::from_coefficients_slice(&[pp.poly_prod.evaluate(&alpha)]); 243 | let prhologN6 = DensePolynomial::from_coefficients_slice(&[pp.lagrange_polynomials_Vn 244 | [pp.logN + 5] 245 | .evaluate(&alpha)]); 246 | 247 | // p_alpha(X) = - zVn(alpha) h(X) 248 | let mut p_alpha_poly = &pz_alpha * &h_hat_poly; 249 | 250 | // p_alpha(X) = p_alpha(X) + ( f(X) - z(X) )(rho1(alpha) + rho2(alpha)) 251 | p_alpha_poly += &(&(&f_poly - &a_poly) * &prho1_add_2); 252 | 253 | // p_alpha(X) = p_alpha(X) + ( (1-sigma) f(X) - v2 + v1 ) rho3(alpha) 254 | p_alpha_poly += 255 | &(&(&(&(&DensePolynomial::from_coefficients_slice(&[(E::Fr::one() - sigma)]) * &f_poly) 256 | - &pv2) 257 | + &pv1) 258 | * &prho3); 259 | 260 | // p_alpha(X) = p_alpha(X) + ( f(X) + v2 - sigma v1 ) rho4(alpha) 261 | p_alpha_poly += &(&(&(&(&DensePolynomial::from_coefficients_slice(&[-sigma]) * &pv1) + &pv2) 262 | + &f_poly) 263 | * &prho4); 264 | 265 | // p_alpha(X) = p_alpha(X) + ( v1 f(X) - v2 ) rho5(alpha) 266 | p_alpha_poly += &(&(&(&f_poly * &pv1) - &pv2) * &prho5); 267 | 268 | // p_alpha(X) = p_alpha(X) + ( f(X) - v1^2 ) prod_(i not in [5, .. , logN + 269 | // 4]) (alpha - sigma^i) 270 | p_alpha_poly += &(&(&f_poly - &(&pv1 * &pv1)) * &ppolyprod); 271 | 272 | // Differing slightly from paper 273 | // Paper uses p_alpha(X) = p_alpha(X) + ( v1 - 1 ) rho_(n)(alpha) assuming that 274 | // logN = n - 6 We use p_alpha(X) = p_alpha(X) + ( v1 - 1 ) rho_(logN + 275 | // 6)(alpha) to allow for any value of logN 276 | p_alpha_poly += 277 | &(&(&pv1 - &(DensePolynomial::from_coefficients_slice(&[E::Fr::one()]))) * &prhologN6); 278 | 279 | //////////////////////////// 280 | // Compute opening proofs 281 | //////////////////////////// 282 | 283 | // KZG.Open(srs_KZG, f(X), deg = bot, (alpha1, alpha2)) 284 | let (_evals1, pi1) = 285 | KZGCommit::open_g1_batch(&pp.poly_ck, &f_poly, None, [alpha1, alpha2].as_ref()); 286 | 287 | // KZG.Open(srs_KZG, p_alpha(X), deg = bot, alpha) 288 | let (evals2, pi2) = KZGCommit::open_g1_batch(&pp.poly_ck, &p_alpha_poly, None, &[alpha]); 289 | 290 | // abort if p_alpha( alpha) != 0 291 | assert!( 292 | evals2[0] == E::Fr::zero(), 293 | "p_alpha(X) does not equal 0 at alpha" 294 | ); 295 | 296 | end_timer!(timer); 297 | CaulkProofUnity { 298 | g1_F, 299 | g1_H, 300 | v1, 301 | v2, 302 | pi1, 303 | pi2, 304 | } 305 | } 306 | 307 | // Verify that the prover knows a, b such that g2_z = g2^(a x - b) and a^n = b^n 308 | #[allow(non_snake_case)] 309 | pub fn caulk_single_unity_verify( 310 | vk: &VerifierPublicParametersUnity, 311 | transcript: &mut CaulkTranscript, 312 | g2_z: &E::G2Affine, 313 | proof: &CaulkProofUnity, 314 | ) -> bool { 315 | let timer = start_timer!(|| "single unity verify"); 316 | 317 | // g2_z must not be the identity 318 | assert!(!g2_z.is_zero(), "g2_z is the identity"); 319 | 320 | // roots of unity in domain of size m = log1_2(n) + 6 321 | let sigma = vk.domain_Vn.element(1); 322 | let v1 = proof.v1; 323 | let v2 = proof.v2; 324 | 325 | //////////////////////////// 326 | // alpha = Hash(A, F, H) 327 | //////////////////////////// 328 | transcript.append_element(b"F", &proof.g1_F); 329 | transcript.append_element(b"H", &proof.g1_H); 330 | transcript.append_element(b"z", g2_z); 331 | let alpha = transcript.get_and_append_challenge(b"alpha"); 332 | 333 | // alpha1 = sigma^(-1) alpha and alpha2 = sigma^(-2) alpha 334 | let alpha1: E::Fr = alpha * vk.domain_Vn.element(vk.domain_Vn.size() - 1); 335 | let alpha2: E::Fr = alpha * vk.domain_Vn.element(vk.domain_Vn.size() - 2); 336 | 337 | /////////////////////////////// 338 | // KZG opening check 339 | /////////////////////////////// 340 | 341 | let check1 = KZGCommit::::verify_g1( 342 | [vk.g1, vk.g1_x].as_ref(), 343 | &vk.powers_of_g2, 344 | &proof.g1_F, 345 | None, 346 | [alpha1, alpha2].as_ref(), 347 | [proof.v1, proof.v2].as_ref(), 348 | &proof.pi1, 349 | ); 350 | 351 | /////////////////////////////// 352 | // Compute P = commitment to p_alpha(X) 353 | /////////////////////////////// 354 | 355 | // Useful field elements. 356 | 357 | // zalpha = z(alpha) = alpha^n - 1, 358 | let zalpha = vk.domain_Vn.vanishing_polynomial().evaluate(&alpha); 359 | 360 | // rhoi = L_i(alpha) = ls_i * [(X^m - 1) / (alpha - w^i) ] 361 | // where ls_i = lagrange_scalars_Vn[i] = prod_{j neq i} (w_i - w_j)^(-1) 362 | let rho0 = 363 | zalpha * (alpha - vk.domain_Vn.element(0)).inverse().unwrap() * vk.lagrange_scalars_Vn[0]; 364 | let rho1 = 365 | zalpha * (alpha - vk.domain_Vn.element(1)).inverse().unwrap() * vk.lagrange_scalars_Vn[1]; 366 | let rho2 = 367 | zalpha * (alpha - vk.domain_Vn.element(2)).inverse().unwrap() * vk.lagrange_scalars_Vn[2]; 368 | let rho3 = 369 | zalpha * (alpha - vk.domain_Vn.element(3)).inverse().unwrap() * vk.lagrange_scalars_Vn[3]; 370 | let rho4 = 371 | zalpha * (alpha - vk.domain_Vn.element(4)).inverse().unwrap() * vk.lagrange_scalars_Vn[4]; 372 | let rhologN5 = zalpha 373 | * (alpha - vk.domain_Vn.element(vk.logN + 5)) 374 | .inverse() 375 | .unwrap() 376 | * vk.lagrange_scalars_Vn[5]; 377 | 378 | // pprod = prod_(i not in [5,..,logN+4]) (alpha - w^i) 379 | let pprod = vk.poly_prod.evaluate(&alpha); 380 | 381 | /////////////////////////////// 382 | // Pairing checks 383 | /////////////////////////////// 384 | 385 | // P = H^(-z(alpha)) * F^(rho0(alpha) + L_1(alpha) + (1 - w)L_2(alpha) + 386 | // L_3(alpha) + v1 L_4(alpha) + prod_(i not in 387 | // [5,..,logN+4]) (alpha - w^i)) 388 | // * g^( (v1 -v2)L_2(alpha) + (v2 - w v1)L_3(alpha) - v2 389 | // L_4(alpha) + (v1 - 1)L_(logN+5)(alpha) 390 | // - v1^2 * prod_(i not in [5,..,logN+4]) (alpha - w^i) ) 391 | let g1_p = proof.g1_H.mul(-zalpha) 392 | + proof 393 | .g1_F 394 | .mul(rho0 + rho1 + (E::Fr::one() - sigma) * rho2 + rho3 + v1 * rho4 + pprod) 395 | + vk.g1.mul( 396 | (v1 - v2) * rho2 + (v2 - sigma * v1) * rho3 - v2 * rho4 397 | + (v1 - E::Fr::one()) * rhologN5 398 | - v1 * v1 * pprod, 399 | ); 400 | 401 | let g1_q = proof.pi2; 402 | 403 | // check that e(P Q3^(-alpha), g2)e( g^(-(rho0 + rho1) - zH(alpha) x^(d-1)), A ) 404 | // e( Q3, g2^x ) = 1 Had to move A from affine to projective and back to 405 | // affine to get it to compile. No idea what difference this makes. 406 | let eq1 = vec![ 407 | ( 408 | (g1_p + g1_q.mul(alpha)).into_affine().into(), 409 | vk.poly_vk.prepared_h.clone(), 410 | ), 411 | ( 412 | ((vk.g1.mul(-rho0 - rho1) + vk.gxpen.mul(-zalpha)).into_affine()).into(), 413 | (*g2_z).into(), 414 | ), 415 | ((-g1_q).into(), vk.poly_vk.prepared_beta_h.clone()), 416 | ]; 417 | 418 | let check2 = E::product_of_pairings(&eq1).is_one(); 419 | end_timer!(timer); 420 | check1 && check2 421 | } 422 | -------------------------------------------------------------------------------- /src/transcript.rs: -------------------------------------------------------------------------------- 1 | use ark_ff::PrimeField; 2 | use ark_serialize::CanonicalSerialize; 3 | use merlin::Transcript; 4 | use std::marker::PhantomData; 5 | 6 | pub struct CaulkTranscript { 7 | transcript: Transcript, 8 | phantom: PhantomData, 9 | } 10 | 11 | impl Default for CaulkTranscript { 12 | fn default() -> Self { 13 | Self::new() 14 | } 15 | } 16 | 17 | impl CaulkTranscript { 18 | pub fn new() -> Self { 19 | Self { 20 | transcript: Transcript::new(b"Init Caulk transcript"), 21 | phantom: PhantomData::default(), 22 | } 23 | } 24 | 25 | /// Get a uniform random field element for field size < 384 26 | pub fn get_and_append_challenge(&mut self, label: &'static [u8]) -> F { 27 | let mut bytes = [0u8; 64]; 28 | self.transcript.challenge_bytes(label, &mut bytes); 29 | let challenge = F::from_le_bytes_mod_order(bytes.as_ref()); 30 | self.append_element(b"append challenge", &challenge); 31 | challenge 32 | } 33 | 34 | /// Append a field/group element to the transcript 35 | pub fn append_element(&mut self, label: &'static [u8], r: &T) { 36 | let mut buf = vec![]; 37 | r.serialize(&mut buf).unwrap(); 38 | self.transcript.append_message(label, buf.as_ref()); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/util.rs: -------------------------------------------------------------------------------- 1 | use ark_ec::PairingEngine; 2 | use ark_ff::PrimeField; 3 | use ark_poly::UVPolynomial; 4 | use ark_poly_commit::kzg10::*; 5 | #[cfg(feature = "parallel")] 6 | use rayon::iter::{IntoParallelRefIterator, ParallelIterator}; 7 | 8 | // Reduces full srs down to smaller srs for smaller polynomials 9 | // Copied from arkworks library (where same function is private) 10 | pub(crate) fn trim>( 11 | srs: &UniversalParams, 12 | mut supported_degree: usize, 13 | ) -> (Powers<'static, E>, VerifierKey) { 14 | if supported_degree == 1 { 15 | supported_degree += 1; 16 | } 17 | 18 | let powers_of_g = srs.powers_of_g[..=supported_degree].to_vec(); 19 | let powers_of_gamma_g = (0..=supported_degree) 20 | .map(|i| srs.powers_of_gamma_g[&i]) 21 | .collect(); 22 | 23 | let powers = Powers { 24 | powers_of_g: ark_std::borrow::Cow::Owned(powers_of_g), 25 | powers_of_gamma_g: ark_std::borrow::Cow::Owned(powers_of_gamma_g), 26 | }; 27 | let vk = VerifierKey { 28 | g: srs.powers_of_g[0], 29 | gamma_g: srs.powers_of_gamma_g[&0], 30 | h: srs.h, 31 | beta_h: srs.beta_h, 32 | prepared_h: srs.prepared_h.clone(), 33 | prepared_beta_h: srs.prepared_beta_h.clone(), 34 | }; 35 | (powers, vk) 36 | } 37 | 38 | //////////////////////////////////////////////// 39 | // 40 | 41 | // copied from arkworks 42 | pub(crate) fn convert_to_bigints(p: &[F]) -> Vec { 43 | ark_std::cfg_iter!(p) 44 | .map(|s| s.into_repr()) 45 | .collect::>() 46 | } 47 | --------------------------------------------------------------------------------