├── .gitignore ├── COPYRIGHT ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── examples └── paper.rs └── src ├── batch.rs ├── lib.rs ├── protocol.rs ├── srs.rs ├── synthesis.rs └── util.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | Cargo.lock 4 | -------------------------------------------------------------------------------- /COPYRIGHT: -------------------------------------------------------------------------------- 1 | Copyrights in the "sonic" library are retained by their contributors. No 2 | copyright assignment is required to contribute to the "sonic" library. 3 | 4 | The "sonic" library is licensed under either of 5 | 6 | * Apache License, Version 2.0, (see ./LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0) 7 | * MIT license (see ./LICENSE-MIT or http://opensource.org/licenses/MIT) 8 | 9 | at your option. 10 | 11 | Unless you explicitly state otherwise, any contribution intentionally 12 | submitted for inclusion in the work by you, as defined in the Apache-2.0 13 | license, shall be dual licensed as above, without any additional terms or 14 | conditions. 15 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "sonic" 3 | version = "0.1.0" 4 | authors = ["Sean Bowe "] 5 | edition = "2018" 6 | 7 | [dependencies.pairing] 8 | version = "0.14" 9 | features = ["u128-support"] 10 | 11 | [dependencies] 12 | merlin = "1" 13 | rand = "0.4" 14 | bellman = "0.1.0" 15 | 16 | [dependencies.sapling-crypto] 17 | git = "https://github.com/zcash-hackworks/sapling-crypto" 18 | rev = "eb409fa3cf8df215ae8d35f5e385751a0c5ffb85" 19 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Permission is hereby granted, free of charge, to any 2 | person obtaining a copy of this software and associated 3 | documentation files (the "Software"), to deal in the 4 | Software without restriction, including without 5 | limitation the rights to use, copy, modify, merge, 6 | publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software 8 | is furnished to do so, subject to the following 9 | conditions: 10 | 11 | The above copyright notice and this permission notice 12 | shall be included in all copies or substantial portions 13 | of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 16 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 17 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 18 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 19 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 22 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Sonic 2 | 3 | This crate is an implementation of **Sonic**, a protocol for quickly verifiable, compact zero-knowledge proofs of arbitrary computations. Sonic is intended to be an alternative to zk-SNARKs which typically require a trusted setup for each individual computation. Sonic is _universal_ in that it requires a single setup, and _updateable_ in that the parameters can be continually strengthened. In exchange, Sonic proofs are slightly longer and slightly slower to verify in practice. 4 | 5 | **THIS IMPLEMENTATION IS A PROTOTYPE AND IS FULL OF BUGS, DO NOT USE IT IN PRODUCTION** 6 | 7 | ## License 8 | 9 | Licensed under either of 10 | 11 | * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) 12 | * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) 13 | 14 | at your option. 15 | 16 | ### Contribution 17 | 18 | Unless you explicitly state otherwise, any contribution intentionally 19 | submitted for inclusion in the work by you, as defined in the Apache-2.0 20 | license, shall be dual licensed as above, without any additional terms or 21 | conditions. 22 | -------------------------------------------------------------------------------- /examples/paper.rs: -------------------------------------------------------------------------------- 1 | extern crate bellman; 2 | extern crate pairing; 3 | extern crate rand; 4 | extern crate sapling_crypto; 5 | extern crate sonic; 6 | 7 | use pairing::{Engine, Field, PrimeField, CurveProjective}; 8 | use sonic::protocol::*; 9 | use sonic::srs::SRS; 10 | use sonic::{Circuit, ConstraintSystem, LinearCombination, SynthesisError, Variable, Coeff}; 11 | use sonic::synthesis::*; 12 | use std::marker::PhantomData; 13 | 14 | struct Adaptor<'a, E: Engine, CS: ConstraintSystem + 'a> { 15 | cs: &'a mut CS, 16 | _marker: PhantomData, 17 | } 18 | 19 | impl<'a, E: Engine, CS: ConstraintSystem + 'a> bellman::ConstraintSystem 20 | for Adaptor<'a, E, CS> 21 | { 22 | type Root = Self; 23 | 24 | fn one() -> bellman::Variable { 25 | bellman::Variable::new_unchecked(bellman::Index::Input(1)) 26 | } 27 | 28 | fn alloc(&mut self, _: A, f: F) -> Result 29 | where 30 | F: FnOnce() -> Result, 31 | A: FnOnce() -> AR, 32 | AR: Into, 33 | { 34 | let var = self.cs.alloc(|| { 35 | f().map_err(|_| SynthesisError::AssignmentMissing) 36 | }).map_err(|_| bellman::SynthesisError::AssignmentMissing)?; 37 | 38 | Ok(match var { 39 | Variable::A(index) => bellman::Variable::new_unchecked(bellman::Index::Input(index)), 40 | Variable::B(index) => bellman::Variable::new_unchecked(bellman::Index::Aux(index)), 41 | _ => unreachable!(), 42 | }) 43 | } 44 | 45 | fn alloc_input( 46 | &mut self, 47 | _: A, 48 | f: F, 49 | ) -> Result 50 | where 51 | F: FnOnce() -> Result, 52 | A: FnOnce() -> AR, 53 | AR: Into, 54 | { 55 | let var = self.cs.alloc_input(|| { 56 | f().map_err(|_| SynthesisError::AssignmentMissing) 57 | }).map_err(|_| bellman::SynthesisError::AssignmentMissing)?; 58 | 59 | Ok(match var { 60 | Variable::A(index) => bellman::Variable::new_unchecked(bellman::Index::Input(index)), 61 | Variable::B(index) => bellman::Variable::new_unchecked(bellman::Index::Aux(index)), 62 | _ => unreachable!(), 63 | }) 64 | } 65 | 66 | fn enforce(&mut self, _: A, a: LA, b: LB, c: LC) 67 | where 68 | A: FnOnce() -> AR, 69 | AR: Into, 70 | LA: FnOnce(bellman::LinearCombination) -> bellman::LinearCombination, 71 | LB: FnOnce(bellman::LinearCombination) -> bellman::LinearCombination, 72 | LC: FnOnce(bellman::LinearCombination) -> bellman::LinearCombination, 73 | { 74 | fn convert(lc: bellman::LinearCombination) -> LinearCombination { 75 | let mut ret = LinearCombination::zero(); 76 | 77 | for &(v, coeff) in lc.as_ref().iter() { 78 | let var = match v.get_unchecked() { 79 | bellman::Index::Input(i) => Variable::A(i), 80 | bellman::Index::Aux(i) => Variable::B(i), 81 | }; 82 | 83 | ret = ret + (Coeff::Full(coeff), var); 84 | } 85 | 86 | ret 87 | } 88 | 89 | fn eval>( 90 | lc: &LinearCombination, 91 | cs: &CS, 92 | ) -> Option { 93 | let mut ret = E::Fr::zero(); 94 | 95 | for &(v, coeff) in lc.as_ref().iter() { 96 | let mut tmp = match cs.get_value(v) { 97 | Ok(tmp) => tmp, 98 | Err(_) => return None, 99 | }; 100 | coeff.multiply(&mut tmp); 101 | ret.add_assign(&tmp); 102 | } 103 | 104 | Some(ret) 105 | } 106 | 107 | let a_lc = convert(a(bellman::LinearCombination::zero())); 108 | let a_value = eval(&a_lc, &*self.cs); 109 | let b_lc = convert(b(bellman::LinearCombination::zero())); 110 | let b_value = eval(&b_lc, &*self.cs); 111 | let c_lc = convert(c(bellman::LinearCombination::zero())); 112 | let c_value = eval(&c_lc, &*self.cs); 113 | 114 | let (a, b, c) = self 115 | .cs 116 | .multiply(|| Ok((a_value.unwrap(), b_value.unwrap(), c_value.unwrap()))) 117 | .unwrap(); 118 | 119 | self.cs.enforce_zero(a_lc - a); 120 | self.cs.enforce_zero(b_lc - b); 121 | self.cs.enforce_zero(c_lc - c); 122 | } 123 | 124 | fn push_namespace(&mut self, _: N) 125 | where 126 | NR: Into, 127 | N: FnOnce() -> NR, 128 | { 129 | // Do nothing; we don't care about namespaces in this context. 130 | } 131 | 132 | fn pop_namespace(&mut self) { 133 | // Do nothing; we don't care about namespaces in this context. 134 | } 135 | 136 | fn get_root(&mut self) -> &mut Self::Root { 137 | self 138 | } 139 | } 140 | 141 | struct AdaptorCircuit(T); 142 | 143 | impl<'a, E: Engine, C: bellman::Circuit + Clone> Circuit for AdaptorCircuit { 144 | fn synthesize>(&self, cs: &mut CS) -> Result<(), SynthesisError> { 145 | let mut adaptor = Adaptor { 146 | cs: cs, 147 | _marker: PhantomData, 148 | }; 149 | 150 | match self.0.clone().synthesize(&mut adaptor) { 151 | Err(_) => return Err(SynthesisError::AssignmentMissing), 152 | Ok(_) => {} 153 | }; 154 | 155 | Ok(()) 156 | } 157 | } 158 | 159 | fn main() { 160 | use pairing::bls12_381::{Bls12, Fr}; 161 | use std::time::{Instant}; 162 | 163 | let srs_x = Fr::from_str("23923").unwrap(); 164 | let srs_alpha = Fr::from_str("23728792").unwrap(); 165 | println!("making srs"); 166 | let start = Instant::now(); 167 | let srs = SRS::::dummy(830564, srs_x, srs_alpha); 168 | println!("done in {:?}", start.elapsed()); 169 | 170 | struct PedersenHashPreimageCircuit<'a, E: sapling_crypto::jubjub::JubjubEngine + 'a> { 171 | preimage: Vec>, 172 | params: &'a E::Params, 173 | } 174 | 175 | impl<'a, E: sapling_crypto::jubjub::JubjubEngine + 'a> Clone for PedersenHashPreimageCircuit<'a, E> { 176 | fn clone(&self) -> Self { 177 | PedersenHashPreimageCircuit { 178 | preimage: self.preimage.clone(), 179 | params: self.params 180 | } 181 | } 182 | } 183 | 184 | impl<'a, E: sapling_crypto::jubjub::JubjubEngine> bellman::Circuit for PedersenHashPreimageCircuit<'a, E> { 185 | fn synthesize>( 186 | self, 187 | cs: &mut CS 188 | ) -> Result<(), bellman::SynthesisError> 189 | { 190 | //use bellman::ConstraintSystem; 191 | use sapling_crypto::circuit::boolean::{AllocatedBit, Boolean}; 192 | use sapling_crypto::circuit::pedersen_hash; 193 | 194 | let mut preimage = vec![]; 195 | 196 | for &bit in self.preimage.iter() { 197 | preimage.push(Boolean::from(AllocatedBit::alloc(&mut* cs, bit)?)); 198 | } 199 | 200 | pedersen_hash::pedersen_hash( 201 | &mut* cs, pedersen_hash::Personalization::NoteCommitment, &preimage, self.params)?; 202 | 203 | Ok(()) 204 | } 205 | } 206 | 207 | #[derive(Clone)] 208 | struct SHA256PreimageCircuit { 209 | preimage: Vec>, 210 | } 211 | 212 | impl bellman::Circuit for SHA256PreimageCircuit { 213 | fn synthesize>( 214 | self, 215 | cs: &mut CS, 216 | ) -> Result<(), bellman::SynthesisError> { 217 | //use bellman::ConstraintSystem; 218 | use sapling_crypto::circuit::boolean::{AllocatedBit, Boolean}; 219 | use sapling_crypto::circuit::sha256::sha256_block_no_padding; 220 | 221 | let mut preimage = vec![]; 222 | 223 | for &bit in self.preimage.iter() { 224 | preimage.push(Boolean::from(AllocatedBit::alloc(&mut *cs, bit)?)); 225 | } 226 | 227 | sha256_block_no_padding(&mut *cs, &preimage)?; 228 | // sha256_block_no_padding(&mut *cs, &preimage)?; 229 | // sha256_block_no_padding(&mut *cs, &preimage)?; 230 | // sha256_block_no_padding(&mut *cs, &preimage)?; 231 | 232 | Ok(()) 233 | } 234 | } 235 | 236 | { 237 | use pairing::{CurveAffine}; 238 | use pairing::bls12_381::{G1Affine, G2Affine}; 239 | let a = G1Affine::one(); 240 | let b = G2Affine::one(); 241 | let c = G1Affine::one(); 242 | 243 | let alpha = G1Affine::one(); 244 | let beta = G2Affine::one(); 245 | let iv = G1Affine::one(); 246 | let gamma = G2Affine::one().prepare(); 247 | let delta = G2Affine::one().prepare(); 248 | 249 | let alphabeta = ::pairing(alpha, beta); 250 | 251 | println!("verifying an idealized groth16 proof"); 252 | let start = Instant::now(); 253 | assert!(::final_exponentiation( 254 | &::miller_loop([ 255 | (&a.prepare(), &b.prepare()), 256 | (&iv.prepare(), &gamma), 257 | (&c.prepare(), &delta), 258 | ].into_iter()) 259 | ).unwrap() != alphabeta); 260 | println!("done in {:?}", start.elapsed()); 261 | } 262 | 263 | { 264 | use sonic::util::multiexp; 265 | use pairing::{CurveAffine}; 266 | use pairing::bls12_381::{G1Affine, G2Affine}; 267 | // e([\alpha G], [\beta H]) = e(A, B) e(IV, [\gamma] H) e(C, [\delta] H) 268 | let a = G1Affine::one(); 269 | let b = G2Affine::one(); 270 | let c = vec![G1Affine::one(); 100]; 271 | let mut tmp = Fr::one(); 272 | tmp.double(); 273 | tmp = tmp.inverse().unwrap(); 274 | let cscalars = (0..100).map(|_| {tmp.square(); tmp}).collect::>(); 275 | 276 | let alpha = G1Affine::one(); 277 | let beta = G2Affine::one(); 278 | let iv = G1Affine::one(); 279 | let gamma = G2Affine::one().prepare(); 280 | let delta = G2Affine::one().prepare(); 281 | 282 | let alphabeta = ::pairing(alpha, beta); 283 | 284 | println!("verifying 100 idealized groth16 proofs"); 285 | let start = Instant::now(); 286 | let c = multiexp( 287 | c.iter(), 288 | cscalars.iter(), 289 | ).into_affine(); 290 | assert!(::final_exponentiation( 291 | &::miller_loop([ 292 | (&a.prepare(), &b.prepare()), 293 | (&a.prepare(), &b.prepare()), 294 | (&a.prepare(), &b.prepare()), 295 | (&a.prepare(), &b.prepare()), 296 | (&a.prepare(), &b.prepare()), 297 | (&a.prepare(), &b.prepare()), 298 | (&a.prepare(), &b.prepare()), 299 | (&a.prepare(), &b.prepare()), 300 | (&a.prepare(), &b.prepare()), 301 | (&a.prepare(), &b.prepare()), 302 | (&a.prepare(), &b.prepare()), 303 | (&a.prepare(), &b.prepare()), 304 | (&a.prepare(), &b.prepare()), 305 | (&a.prepare(), &b.prepare()), 306 | (&a.prepare(), &b.prepare()), 307 | (&a.prepare(), &b.prepare()), 308 | (&a.prepare(), &b.prepare()), 309 | (&a.prepare(), &b.prepare()), 310 | (&a.prepare(), &b.prepare()), 311 | (&a.prepare(), &b.prepare()), 312 | (&a.prepare(), &b.prepare()), 313 | (&a.prepare(), &b.prepare()), 314 | (&a.prepare(), &b.prepare()), 315 | (&a.prepare(), &b.prepare()), 316 | (&a.prepare(), &b.prepare()), 317 | (&a.prepare(), &b.prepare()), 318 | (&a.prepare(), &b.prepare()), 319 | (&a.prepare(), &b.prepare()), 320 | (&a.prepare(), &b.prepare()), 321 | (&a.prepare(), &b.prepare()), 322 | (&a.prepare(), &b.prepare()), 323 | (&a.prepare(), &b.prepare()), 324 | (&a.prepare(), &b.prepare()), 325 | (&a.prepare(), &b.prepare()), 326 | (&a.prepare(), &b.prepare()), 327 | (&a.prepare(), &b.prepare()), 328 | (&a.prepare(), &b.prepare()), 329 | (&a.prepare(), &b.prepare()), 330 | (&a.prepare(), &b.prepare()), 331 | (&a.prepare(), &b.prepare()), 332 | (&a.prepare(), &b.prepare()), 333 | (&a.prepare(), &b.prepare()), 334 | (&a.prepare(), &b.prepare()), 335 | (&a.prepare(), &b.prepare()), 336 | (&a.prepare(), &b.prepare()), 337 | (&a.prepare(), &b.prepare()), 338 | (&a.prepare(), &b.prepare()), 339 | (&a.prepare(), &b.prepare()), 340 | (&a.prepare(), &b.prepare()), 341 | (&a.prepare(), &b.prepare()), 342 | (&a.prepare(), &b.prepare()), 343 | (&a.prepare(), &b.prepare()), 344 | (&a.prepare(), &b.prepare()), 345 | (&a.prepare(), &b.prepare()), 346 | (&a.prepare(), &b.prepare()), 347 | (&a.prepare(), &b.prepare()), 348 | (&a.prepare(), &b.prepare()), 349 | (&a.prepare(), &b.prepare()), 350 | (&a.prepare(), &b.prepare()), 351 | (&a.prepare(), &b.prepare()), 352 | (&a.prepare(), &b.prepare()), 353 | (&a.prepare(), &b.prepare()), 354 | (&a.prepare(), &b.prepare()), 355 | (&a.prepare(), &b.prepare()), 356 | (&a.prepare(), &b.prepare()), 357 | (&a.prepare(), &b.prepare()), 358 | (&a.prepare(), &b.prepare()), 359 | (&a.prepare(), &b.prepare()), 360 | (&a.prepare(), &b.prepare()), 361 | (&a.prepare(), &b.prepare()), 362 | (&a.prepare(), &b.prepare()), 363 | (&a.prepare(), &b.prepare()), 364 | (&a.prepare(), &b.prepare()), 365 | (&a.prepare(), &b.prepare()), 366 | (&a.prepare(), &b.prepare()), 367 | (&a.prepare(), &b.prepare()), 368 | (&a.prepare(), &b.prepare()), 369 | (&a.prepare(), &b.prepare()), 370 | (&a.prepare(), &b.prepare()), 371 | (&a.prepare(), &b.prepare()), 372 | (&a.prepare(), &b.prepare()), 373 | (&a.prepare(), &b.prepare()), 374 | (&a.prepare(), &b.prepare()), 375 | (&a.prepare(), &b.prepare()), 376 | (&a.prepare(), &b.prepare()), 377 | (&a.prepare(), &b.prepare()), 378 | (&a.prepare(), &b.prepare()), 379 | (&a.prepare(), &b.prepare()), 380 | (&a.prepare(), &b.prepare()), 381 | (&a.prepare(), &b.prepare()), 382 | (&a.prepare(), &b.prepare()), 383 | (&a.prepare(), &b.prepare()), 384 | (&a.prepare(), &b.prepare()), 385 | (&a.prepare(), &b.prepare()), 386 | (&a.prepare(), &b.prepare()), 387 | (&a.prepare(), &b.prepare()), 388 | (&a.prepare(), &b.prepare()), 389 | (&a.prepare(), &b.prepare()), 390 | (&a.prepare(), &b.prepare()), 391 | (&a.prepare(), &b.prepare()), 392 | (&iv.prepare(), &gamma), 393 | (&c.prepare(), &delta), 394 | ].into_iter()) 395 | ).unwrap() != alphabeta); 396 | println!("done in {:?}", start.elapsed()); 397 | } 398 | 399 | { 400 | type ChosenBackend = Permutation3; 401 | 402 | let samples: usize = 5; 403 | 404 | // const NUM_BITS: usize = 384; 405 | // let params = sapling_crypto::jubjub::JubjubBls12::new(); 406 | // let circuit = PedersenHashPreimageCircuit { 407 | // preimage: vec![Some(true); NUM_BITS], 408 | // params: ¶ms 409 | // }; 410 | 411 | let circuit = SHA256PreimageCircuit { 412 | preimage: vec![Some(true); 512], 413 | }; 414 | 415 | println!("creating proof"); 416 | let start = Instant::now(); 417 | let proof = create_proof::(&AdaptorCircuit(circuit.clone()), &srs).unwrap(); 418 | println!("done in {:?}", start.elapsed()); 419 | 420 | println!("creating advice"); 421 | let start = Instant::now(); 422 | let advice = create_advice::(&AdaptorCircuit(circuit.clone()), &proof, &srs); 423 | println!("done in {:?}", start.elapsed()); 424 | 425 | println!("creating aggregate for {} proofs", samples); 426 | let start = Instant::now(); 427 | let proofs: Vec<_> = (0..samples).map(|_| (proof.clone(), advice.clone())).collect(); 428 | let aggregate = create_aggregate::(&AdaptorCircuit(circuit.clone()), &proofs, &srs); 429 | println!("done in {:?}", start.elapsed()); 430 | 431 | { 432 | let mut verifier = MultiVerifier::::new(AdaptorCircuit(circuit.clone()), &srs).unwrap(); 433 | println!("verifying 1 proof without advice"); 434 | let start = Instant::now(); 435 | { 436 | for _ in 0..1 { 437 | verifier.add_proof(&proof, &[], |_, _| None); 438 | } 439 | assert_eq!(verifier.check_all(), true); // TODO 440 | } 441 | println!("done in {:?}", start.elapsed()); 442 | } 443 | 444 | { 445 | let mut verifier = MultiVerifier::::new(AdaptorCircuit(circuit.clone()), &srs).unwrap(); 446 | println!("verifying {} proofs without advice", samples); 447 | let start = Instant::now(); 448 | { 449 | for _ in 0..samples { 450 | verifier.add_proof(&proof, &[], |_, _| None); 451 | } 452 | assert_eq!(verifier.check_all(), true); // TODO 453 | } 454 | println!("done in {:?}", start.elapsed()); 455 | } 456 | 457 | { 458 | let mut verifier = MultiVerifier::::new(AdaptorCircuit(circuit.clone()), &srs).unwrap(); 459 | println!("verifying 100 proofs with advice"); 460 | let start = Instant::now(); 461 | { 462 | for (ref proof, ref advice) in &proofs { 463 | verifier.add_proof_with_advice(proof, &[], advice); 464 | } 465 | verifier.add_aggregate(&proofs, &aggregate); 466 | assert_eq!(verifier.check_all(), true); // TODO 467 | } 468 | println!("done in {:?}", start.elapsed()); 469 | } 470 | } 471 | } 472 | -------------------------------------------------------------------------------- /src/batch.rs: -------------------------------------------------------------------------------- 1 | //! Our protocol allows the verification of multiple proofs and even 2 | //! of individual proofs to batch the pairing operations such that 3 | //! only a smaller, fixed number of pairings must occur for an entire 4 | //! batch of proofs. This is possible because G2 elements are fixed 5 | //! in our protocol and never appear in proofs; everything can be 6 | //! combined probabilistically. 7 | //! 8 | //! This submodule contains the `Batch` abstraction for creating a 9 | //! context for batch verification. 10 | 11 | use pairing::{Engine, Field, CurveAffine, CurveProjective}; 12 | use crate::srs::SRS; 13 | use crate::util::multiexp; 14 | 15 | // One of the primary functions of the `Batch` abstraction is handling 16 | // Kate commitment openings: 17 | // 18 | // e(P', [\alpha(x - z)] H) = e(P, H) e([-v] G, [\alpha] H) 19 | // ==> e(P', [\alpha x] H) e([-z] P', [\alpha] H) = e(P, H) e([-v] G, [\alpha] H) 20 | // 21 | // Many of these can be opened simultaneously by sampling random `r` and 22 | // accumulating... 23 | // 24 | // e([r] P', [\alpha x] H) 25 | // e([-rz] P', [\alpha] H) 26 | // e([r] P, -H) 27 | // e([rv] G, [\alpha] H) 28 | // 29 | // ... and checking that the result is the identity in the target group. 30 | pub struct Batch { 31 | alpha_x: Vec<(E::G1Affine, E::Fr)>, 32 | alpha_x_precomp: ::Prepared, 33 | 34 | alpha: Vec<(E::G1Affine, E::Fr)>, 35 | alpha_precomp: ::Prepared, 36 | 37 | neg_h: Vec<(E::G1Affine, E::Fr)>, 38 | neg_h_precomp: ::Prepared, 39 | 40 | neg_x_n_minus_d: Vec<(E::G1Affine, E::Fr)>, 41 | neg_x_n_minus_d_precomp: ::Prepared, 42 | 43 | // The value paired with [\alpha] H, accumulated in the field 44 | // to save group operations. 45 | value: E::Fr, 46 | g: E::G1Affine, 47 | } 48 | 49 | impl Batch { 50 | pub fn new(srs: &SRS, n: usize) -> Self { 51 | Batch { 52 | alpha_x: vec![], 53 | alpha_x_precomp: srs.h_positive_x_alpha[1].prepare(), 54 | 55 | alpha: vec![], 56 | alpha_precomp: srs.h_positive_x_alpha[0].prepare(), 57 | 58 | neg_h: vec![], 59 | neg_h_precomp: { 60 | let mut tmp = srs.h_negative_x[0]; 61 | tmp.negate(); 62 | tmp.prepare() 63 | }, 64 | 65 | neg_x_n_minus_d: vec![], 66 | neg_x_n_minus_d_precomp: { 67 | let mut tmp = srs.h_negative_x[srs.d - n]; 68 | tmp.negate(); 69 | tmp.prepare() 70 | }, 71 | 72 | value: E::Fr::zero(), 73 | g: srs.g_positive_x[0], 74 | } 75 | } 76 | 77 | pub fn add_opening(&mut self, p: E::G1Affine, mut r: E::Fr, point: E::Fr) { 78 | self.alpha_x.push((p, r)); 79 | r.mul_assign(&point); 80 | r.negate(); 81 | self.alpha.push((p, r)); 82 | } 83 | 84 | pub fn add_commitment(&mut self, p: E::G1Affine, r: E::Fr) { 85 | self.neg_h.push((p, r)); 86 | } 87 | 88 | pub fn add_commitment_max_n(&mut self, p: E::G1Affine, r: E::Fr) { 89 | self.neg_x_n_minus_d.push((p, r)); 90 | } 91 | 92 | pub fn add_opening_value(&mut self, mut r: E::Fr, point: E::Fr) { 93 | r.mul_assign(&point); 94 | self.value.add_assign(&r); 95 | } 96 | 97 | pub fn check_all(mut self) -> bool { 98 | self.alpha.push((self.g, self.value)); 99 | 100 | let alpha_x = multiexp( 101 | self.alpha_x.iter().map(|x| &x.0), 102 | self.alpha_x.iter().map(|x| &x.1), 103 | ).into_affine().prepare(); 104 | 105 | let alpha = multiexp( 106 | self.alpha.iter().map(|x| &x.0), 107 | self.alpha.iter().map(|x| &x.1), 108 | ).into_affine().prepare(); 109 | 110 | let neg_h = multiexp( 111 | self.neg_h.iter().map(|x| &x.0), 112 | self.neg_h.iter().map(|x| &x.1), 113 | ).into_affine().prepare(); 114 | 115 | let neg_x_n_minus_d = multiexp( 116 | self.neg_x_n_minus_d.iter().map(|x| &x.0), 117 | self.neg_x_n_minus_d.iter().map(|x| &x.1), 118 | ).into_affine().prepare(); 119 | 120 | E::final_exponentiation(&E::miller_loop(&[ 121 | (&alpha_x, &self.alpha_x_precomp), 122 | (&alpha, &self.alpha_precomp), 123 | (&neg_h, &self.neg_h_precomp), 124 | (&neg_x_n_minus_d, &self.neg_x_n_minus_d_precomp), 125 | ])).unwrap() == E::Fqk::one() 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | use pairing::{Engine, Field}; 2 | use std::ops::{Add, Neg, Sub}; 3 | 4 | pub mod srs; 5 | pub mod util; 6 | pub mod batch; 7 | pub mod synthesis; 8 | pub mod protocol; 9 | 10 | #[derive(Copy, Clone, Debug)] 11 | pub enum SynthesisError { 12 | AssignmentMissing, 13 | Violation, 14 | } 15 | 16 | pub trait Circuit { 17 | fn synthesize>(&self, cs: &mut CS) -> Result<(), SynthesisError>; 18 | } 19 | 20 | pub trait ConstraintSystem { 21 | const ONE: Variable; 22 | 23 | fn alloc(&mut self, value: F) -> Result 24 | where 25 | F: FnOnce() -> Result; 26 | 27 | fn alloc_input(&mut self, value: F) -> Result 28 | where 29 | F: FnOnce() -> Result; 30 | 31 | fn enforce_zero(&mut self, lc: LinearCombination); 32 | 33 | fn multiply(&mut self, values: F) -> Result<(Variable, Variable, Variable), SynthesisError> 34 | where 35 | F: FnOnce() -> Result<(E::Fr, E::Fr, E::Fr), SynthesisError>; 36 | 37 | // TODO: get rid of this 38 | fn get_value(&self, _var: Variable) -> Result { 39 | Err(()) 40 | } 41 | } 42 | 43 | #[derive(Copy, Clone, Debug)] 44 | pub enum Variable { 45 | A(usize), 46 | B(usize), 47 | C(usize), 48 | } 49 | 50 | impl Variable { 51 | fn get_index(&self) -> usize { 52 | match *self { 53 | Variable::A(index) => index, 54 | Variable::B(index) => index, 55 | Variable::C(index) => index, 56 | } 57 | } 58 | } 59 | 60 | #[derive(Debug)] 61 | pub enum Coeff { 62 | Zero, 63 | One, 64 | NegativeOne, 65 | Full(E::Fr), 66 | } 67 | 68 | impl Coeff { 69 | pub fn multiply(&self, with: &mut E::Fr) { 70 | match self { 71 | Coeff::Zero => { 72 | *with = E::Fr::zero(); 73 | }, 74 | Coeff::One => {}, 75 | Coeff::NegativeOne => { 76 | with.negate(); 77 | }, 78 | Coeff::Full(val) => { 79 | with.mul_assign(val); 80 | } 81 | } 82 | } 83 | } 84 | 85 | impl Copy for Coeff {} 86 | impl Clone for Coeff { 87 | fn clone(&self) -> Self { 88 | *self 89 | } 90 | } 91 | 92 | impl Neg for Coeff { 93 | type Output = Coeff; 94 | 95 | fn neg(self) -> Self { 96 | match self { 97 | Coeff::Zero => Coeff::Zero, 98 | Coeff::One => Coeff::NegativeOne, 99 | Coeff::NegativeOne => Coeff::One, 100 | Coeff::Full(mut a) => { 101 | a.negate(); 102 | Coeff::Full(a) 103 | } 104 | } 105 | } 106 | } 107 | 108 | /// This represents a linear combination of some variables, with coefficients 109 | /// in the scalar field of a pairing-friendly elliptic curve group. 110 | #[derive(Clone)] 111 | pub struct LinearCombination(Vec<(Variable, Coeff)>); 112 | 113 | impl From for LinearCombination { 114 | fn from(var: Variable) -> LinearCombination { 115 | LinearCombination::::zero() + var 116 | } 117 | } 118 | 119 | impl AsRef<[(Variable, Coeff)]> for LinearCombination { 120 | fn as_ref(&self) -> &[(Variable, Coeff)] { 121 | &self.0 122 | } 123 | } 124 | 125 | impl LinearCombination { 126 | pub fn zero() -> LinearCombination { 127 | LinearCombination(vec![]) 128 | } 129 | } 130 | 131 | impl Add<(Coeff, Variable)> for LinearCombination { 132 | type Output = LinearCombination; 133 | 134 | fn add(mut self, (coeff, var): (Coeff, Variable)) -> LinearCombination { 135 | self.0.push((var, coeff)); 136 | 137 | self 138 | } 139 | } 140 | 141 | impl Sub<(Coeff, Variable)> for LinearCombination { 142 | type Output = LinearCombination; 143 | 144 | fn sub(self, (coeff, var): (Coeff, Variable)) -> LinearCombination { 145 | self + (-coeff, var) 146 | } 147 | } 148 | 149 | impl Add for LinearCombination { 150 | type Output = LinearCombination; 151 | 152 | fn add(self, other: Variable) -> LinearCombination { 153 | self + (Coeff::One, other) 154 | } 155 | } 156 | 157 | impl Sub for LinearCombination { 158 | type Output = LinearCombination; 159 | 160 | fn sub(self, other: Variable) -> LinearCombination { 161 | self - (Coeff::One, other) 162 | } 163 | } 164 | 165 | impl<'a, E: Engine> Add<&'a LinearCombination> for LinearCombination { 166 | type Output = LinearCombination; 167 | 168 | fn add(mut self, other: &'a LinearCombination) -> LinearCombination { 169 | for s in &other.0 { 170 | self = self + (s.1, s.0); 171 | } 172 | 173 | self 174 | } 175 | } 176 | 177 | impl<'a, E: Engine> Sub<&'a LinearCombination> for LinearCombination { 178 | type Output = LinearCombination; 179 | 180 | fn sub(mut self, other: &'a LinearCombination) -> LinearCombination { 181 | for s in &other.0 { 182 | self = self - (s.1, s.0); 183 | } 184 | 185 | self 186 | } 187 | } 188 | -------------------------------------------------------------------------------- /src/protocol.rs: -------------------------------------------------------------------------------- 1 | use pairing::{Engine, Field, CurveProjective}; 2 | use std::marker::PhantomData; 3 | use merlin::{Transcript}; 4 | use crate::util::*; 5 | use crate::batch::Batch; 6 | use crate::synthesis::{Backend, SynthesisDriver}; 7 | use crate::{Circuit, SynthesisError, Variable, Coeff}; 8 | use crate::srs::SRS; 9 | 10 | #[derive(Clone)] 11 | pub struct SxyAdvice { 12 | s: E::G1Affine, 13 | opening: E::G1Affine, 14 | szy: E::Fr, 15 | } 16 | 17 | #[derive(Clone)] 18 | pub struct Proof { 19 | r: E::G1Affine, 20 | t: E::G1Affine, 21 | rz: E::Fr, 22 | rzy: E::Fr, 23 | z_opening: E::G1Affine, 24 | zy_opening: E::G1Affine 25 | } 26 | 27 | pub struct MultiVerifier, S: SynthesisDriver> { 28 | circuit: C, 29 | batch: Batch, 30 | k_map: Vec, 31 | n: usize, 32 | q: usize, 33 | _marker: PhantomData<(E, S)> 34 | } 35 | 36 | impl, S: SynthesisDriver> MultiVerifier { 37 | pub fn new(circuit: C, srs: &SRS) -> Result { 38 | struct Preprocess { 39 | k_map: Vec, 40 | n: usize, 41 | q: usize, 42 | _marker: PhantomData 43 | } 44 | 45 | impl<'a, E: Engine> Backend for &'a mut Preprocess { 46 | type LinearConstraintIndex = (); 47 | 48 | fn get_for_q(&self, q: usize) -> Self::LinearConstraintIndex { () } 49 | 50 | fn new_k_power(&mut self, index: usize) { 51 | self.k_map.push(index); 52 | } 53 | 54 | fn new_multiplication_gate(&mut self) { 55 | self.n += 1; 56 | } 57 | 58 | fn new_linear_constraint(&mut self) { 59 | self.q += 1; 60 | () 61 | } 62 | } 63 | 64 | let mut preprocess = Preprocess { k_map: vec![], n: 0, q: 0, _marker: PhantomData }; 65 | 66 | S::synthesize(&mut preprocess, &circuit)?; 67 | 68 | Ok(MultiVerifier { 69 | circuit, 70 | batch: Batch::new(srs, preprocess.n), 71 | k_map: preprocess.k_map, 72 | n: preprocess.n, 73 | q: preprocess.q, 74 | _marker: PhantomData 75 | }) 76 | } 77 | 78 | pub fn add_aggregate( 79 | &mut self, 80 | proofs: &[(Proof, SxyAdvice)], 81 | aggregate: &Aggregate, 82 | ) 83 | { 84 | let mut transcript = Transcript::new(&[]); 85 | let mut y_values: Vec = Vec::with_capacity(proofs.len()); 86 | for &(ref proof, ref sxyadvice) in proofs { 87 | { 88 | let mut transcript = Transcript::new(&[]); 89 | transcript.commit_point(&proof.r); 90 | y_values.push(transcript.get_challenge_scalar()); 91 | } 92 | 93 | transcript.commit_point(&sxyadvice.s); 94 | } 95 | 96 | let z: E::Fr = transcript.get_challenge_scalar(); 97 | 98 | transcript.commit_point(&aggregate.c); 99 | 100 | let w: E::Fr = transcript.get_challenge_scalar(); 101 | 102 | let szw = { 103 | let mut tmp = SxEval::new(w, self.n); 104 | S::synthesize(&mut tmp, &self.circuit).unwrap(); // TODO 105 | 106 | tmp.finalize(z) 107 | }; 108 | 109 | { 110 | // TODO: like everything else doing this, this isn't really random 111 | let random: E::Fr; 112 | let mut transcript = transcript.clone(); 113 | random = transcript.get_challenge_scalar(); 114 | 115 | self.batch.add_opening(aggregate.opening, random, w); 116 | self.batch.add_commitment(aggregate.c, random); 117 | self.batch.add_opening_value(szw, random); 118 | } 119 | 120 | for ((opening, value), &y) in aggregate.c_openings.iter().zip(y_values.iter()) { 121 | let random: E::Fr; 122 | let mut transcript = transcript.clone(); 123 | random = transcript.get_challenge_scalar(); 124 | 125 | self.batch.add_opening(*opening, random, y); 126 | self.batch.add_commitment(aggregate.c, random); 127 | self.batch.add_opening_value(*value, random); 128 | } 129 | 130 | let random: E::Fr; 131 | { 132 | let mut transcript = transcript.clone(); 133 | random = transcript.get_challenge_scalar(); 134 | } 135 | 136 | let mut expected_value = E::Fr::zero(); 137 | for ((_, advice), c_opening) in proofs.iter().zip(aggregate.c_openings.iter()) { 138 | let mut r: E::Fr = transcript.get_challenge_scalar(); 139 | 140 | // expected value of the later opening 141 | { 142 | let mut tmp = c_opening.1; 143 | tmp.mul_assign(&r); 144 | expected_value.add_assign(&tmp); 145 | } 146 | 147 | r.mul_assign(&random); 148 | 149 | self.batch.add_commitment(advice.s, r); 150 | } 151 | 152 | self.batch.add_opening_value(expected_value, random); 153 | self.batch.add_opening(aggregate.s_opening, random, z); 154 | } 155 | 156 | pub fn add_proof_with_advice( 157 | &mut self, 158 | proof: &Proof, 159 | inputs: &[E::Fr], 160 | advice: &SxyAdvice, 161 | ) 162 | { 163 | let mut z = None; 164 | 165 | self.add_proof(proof, inputs, |_z, _y| { 166 | z = Some(_z); 167 | Some(advice.szy) 168 | }); 169 | 170 | let z = z.unwrap(); 171 | 172 | // We need to open up SxyAdvice.s at z using SxyAdvice.opening 173 | let mut transcript = Transcript::new(&[]); 174 | transcript.commit_point(&advice.opening); 175 | transcript.commit_point(&advice.s); 176 | transcript.commit_scalar(&advice.szy); 177 | let random: E::Fr = transcript.get_challenge_scalar(); 178 | 179 | self.batch.add_opening(advice.opening, random, z); 180 | self.batch.add_commitment(advice.s, random); 181 | self.batch.add_opening_value(advice.szy, random); 182 | } 183 | 184 | pub fn add_proof( 185 | &mut self, 186 | proof: &Proof, 187 | inputs: &[E::Fr], 188 | sxy: F 189 | ) 190 | where F: FnOnce(E::Fr, E::Fr) -> Option 191 | { 192 | let mut transcript = Transcript::new(&[]); 193 | 194 | transcript.commit_point(&proof.r); 195 | 196 | let y: E::Fr = transcript.get_challenge_scalar(); 197 | 198 | transcript.commit_point(&proof.t); 199 | 200 | let z: E::Fr = transcript.get_challenge_scalar(); 201 | 202 | transcript.commit_scalar(&proof.rz); 203 | transcript.commit_scalar(&proof.rzy); 204 | 205 | let r1: E::Fr = transcript.get_challenge_scalar(); 206 | 207 | transcript.commit_point(&proof.z_opening); 208 | transcript.commit_point(&proof.zy_opening); 209 | 210 | // First, the easy one. Let's open up proof.r at zy, using proof.zy_opening 211 | // as the evidence and proof.rzy as the opening. 212 | { 213 | let random = transcript.get_challenge_scalar(); 214 | let mut zy = z; 215 | zy.mul_assign(&y); 216 | self.batch.add_opening(proof.zy_opening, random, zy); 217 | self.batch.add_commitment_max_n(proof.r, random); 218 | self.batch.add_opening_value(proof.rzy, random); 219 | } 220 | 221 | // Now we need to compute t(z, y) with what we have. Let's compute k(y). 222 | let mut ky = E::Fr::zero(); 223 | for (exp, input) in self.k_map.iter().zip(Some(E::Fr::one()).iter().chain(inputs.iter())) { 224 | let mut term = y.pow(&[(*exp + self.n) as u64]); 225 | term.mul_assign(input); 226 | ky.add_assign(&term); 227 | } 228 | 229 | // Compute s(z, y) 230 | let szy = sxy(z, y).unwrap_or_else(|| { 231 | let mut tmp = SxEval::new(y, self.n); 232 | S::synthesize(&mut tmp, &self.circuit).unwrap(); // TODO 233 | 234 | tmp.finalize(z) 235 | 236 | // let mut tmp = SyEval::new(z, self.n, self.q); 237 | // S::synthesize(&mut tmp, &self.circuit).unwrap(); // TODO 238 | 239 | // tmp.finalize(y) 240 | }); 241 | 242 | // Finally, compute t(z, y) 243 | let mut tzy = proof.rzy; 244 | tzy.add_assign(&szy); 245 | tzy.mul_assign(&proof.rz); 246 | tzy.sub_assign(&ky); 247 | 248 | // We open these both at the same time by keeping their commitments 249 | // linearly independent (using r1). 250 | { 251 | let mut random = transcript.get_challenge_scalar(); 252 | 253 | self.batch.add_opening(proof.z_opening, random, z); 254 | self.batch.add_opening_value(tzy, random); 255 | self.batch.add_commitment(proof.t, random); 256 | 257 | random.mul_assign(&r1); 258 | 259 | self.batch.add_opening_value(proof.rz, random); 260 | self.batch.add_commitment_max_n(proof.r, random); 261 | } 262 | } 263 | 264 | pub fn check_all(self) -> bool { 265 | self.batch.check_all() 266 | } 267 | } 268 | 269 | #[derive(Clone)] 270 | pub struct Aggregate { 271 | // Commitment to s(z, Y) 272 | c: E::G1Affine, 273 | // We have to open each of the S commitments to a random point `z` 274 | s_opening: E::G1Affine, 275 | // We have to open C to each constituent `y` 276 | c_openings: Vec<(E::G1Affine, E::Fr)>, 277 | // Then we have to finally open C 278 | opening: E::G1Affine, 279 | } 280 | 281 | pub fn create_aggregate, S: SynthesisDriver>( 282 | circuit: &C, 283 | inputs: &[(Proof, SxyAdvice)], 284 | srs: &SRS, 285 | ) -> Aggregate 286 | { 287 | // TODO: precompute this? 288 | let (n, q) = { 289 | struct CountN { 290 | n: usize, 291 | q: usize 292 | } 293 | 294 | impl<'a, E: Engine> Backend for &'a mut CountN { 295 | type LinearConstraintIndex = (); 296 | 297 | fn get_for_q(&self, q: usize) -> Self::LinearConstraintIndex { () } 298 | 299 | fn new_multiplication_gate(&mut self) { 300 | self.n += 1; 301 | } 302 | 303 | fn new_linear_constraint(&mut self) { 304 | self.q += 1; 305 | () 306 | } 307 | } 308 | 309 | let mut tmp = CountN{n:0,q:0}; 310 | S::synthesize(&mut tmp, circuit).unwrap(); // TODO 311 | 312 | (tmp.n, tmp.q) 313 | }; 314 | 315 | let mut transcript = Transcript::new(&[]); 316 | let mut y_values: Vec = Vec::with_capacity(inputs.len()); 317 | for &(ref proof, ref sxyadvice) in inputs { 318 | { 319 | let mut transcript = Transcript::new(&[]); 320 | transcript.commit_point(&proof.r); 321 | y_values.push(transcript.get_challenge_scalar()); 322 | } 323 | 324 | transcript.commit_point(&sxyadvice.s); 325 | } 326 | 327 | let z: E::Fr = transcript.get_challenge_scalar(); 328 | 329 | // Compute s(z, Y) 330 | let (s_poly_negative, s_poly_positive) = { 331 | let mut tmp = SyEval::new(z, n, q); 332 | S::synthesize(&mut tmp, circuit).unwrap(); // TODO 333 | 334 | tmp.poly() 335 | }; 336 | 337 | // Compute C = g^{s(z, x)} 338 | let c = multiexp( 339 | srs.g_positive_x_alpha[0..(n + q)] 340 | .iter() 341 | .chain_ext(srs.g_negative_x_alpha[0..n].iter()), 342 | s_poly_positive.iter().chain_ext(s_poly_negative.iter()) 343 | ).into_affine(); 344 | 345 | transcript.commit_point(&c); 346 | 347 | // Open C at w 348 | let w: E::Fr = transcript.get_challenge_scalar(); 349 | 350 | let value = compute_value::(&w, &s_poly_positive, &s_poly_negative); 351 | 352 | let opening = { 353 | let mut value = value; 354 | value.negate(); 355 | 356 | let poly = kate_divison( 357 | s_poly_negative.iter().rev().chain_ext(Some(value).iter()).chain_ext(s_poly_positive.iter()), 358 | w, 359 | ); 360 | 361 | let negative_poly = poly[0..n].iter().rev(); 362 | let positive_poly = poly[n..].iter(); 363 | multiexp( 364 | srs.g_negative_x[1..(negative_poly.len() + 1)].iter().chain_ext( 365 | srs.g_positive_x[0..positive_poly.len()].iter() 366 | ), 367 | negative_poly.chain_ext(positive_poly) 368 | ).into_affine() 369 | }; 370 | 371 | // Let's open up C to every y. 372 | fn compute_value(y: &E::Fr, poly_positive: &[E::Fr], poly_negative: &[E::Fr]) -> E::Fr { 373 | let mut value = E::Fr::zero(); 374 | 375 | let yinv = y.inverse().unwrap(); // TODO 376 | let mut tmp = yinv; 377 | for &coeff in poly_negative { 378 | let mut coeff = coeff; 379 | coeff.mul_assign(&tmp); 380 | value.add_assign(&coeff); 381 | tmp.mul_assign(&yinv); 382 | } 383 | 384 | let mut tmp = *y; 385 | for &coeff in poly_positive { 386 | let mut coeff = coeff; 387 | coeff.mul_assign(&tmp); 388 | value.add_assign(&coeff); 389 | tmp.mul_assign(&y); 390 | } 391 | 392 | value 393 | } 394 | 395 | let mut c_openings = vec![]; 396 | for y in &y_values { 397 | let value = compute_value::(y, &s_poly_positive, &s_poly_negative); 398 | 399 | let opening = { 400 | let mut value = value; 401 | value.negate(); 402 | 403 | let poly = kate_divison( 404 | s_poly_negative.iter().rev().chain_ext(Some(value).iter()).chain_ext(s_poly_positive.iter()), 405 | *y, 406 | ); 407 | 408 | let negative_poly = poly[0..n].iter().rev(); 409 | let positive_poly = poly[n..].iter(); 410 | multiexp( 411 | srs.g_negative_x[1..(negative_poly.len() + 1)].iter().chain_ext( 412 | srs.g_positive_x[0..positive_poly.len()].iter() 413 | ), 414 | negative_poly.chain_ext(positive_poly) 415 | ).into_affine() 416 | }; 417 | 418 | c_openings.push((opening, value)); 419 | } 420 | 421 | // Okay, great. Now we need to open up each S at the same point z to the same value. 422 | // Since we're opening up all the S's at the same point, we create a bunch of random 423 | // challenges instead and open up a random linear combination. 424 | 425 | let mut poly_negative = vec![E::Fr::zero(); n]; 426 | let mut poly_positive = vec![E::Fr::zero(); 2*n]; 427 | let mut expected_value = E::Fr::zero(); 428 | 429 | for (y, c_opening) in y_values.iter().zip(c_openings.iter()) { 430 | // Compute s(X, y_i) 431 | let (s_poly_negative, s_poly_positive) = { 432 | let mut tmp = SxEval::new(*y, n); 433 | S::synthesize(&mut tmp, circuit).unwrap(); // TODO 434 | 435 | tmp.poly() 436 | }; 437 | 438 | let mut value = c_opening.1; 439 | let r: E::Fr = transcript.get_challenge_scalar(); 440 | value.mul_assign(&r); 441 | expected_value.add_assign(&value); 442 | 443 | for (mut coeff, target) in s_poly_negative.into_iter().zip(poly_negative.iter_mut()) { 444 | coeff.mul_assign(&r); 445 | target.add_assign(&coeff); 446 | } 447 | 448 | for (mut coeff, target) in s_poly_positive.into_iter().zip(poly_positive.iter_mut()) { 449 | coeff.mul_assign(&r); 450 | target.add_assign(&coeff); 451 | } 452 | } 453 | 454 | let s_opening = { 455 | let mut value = expected_value; 456 | value.negate(); 457 | 458 | let poly = kate_divison( 459 | poly_negative.iter().rev().chain_ext(Some(value).iter()).chain_ext(poly_positive.iter()), 460 | z, 461 | ); 462 | 463 | let negative_poly = poly[0..n].iter().rev(); 464 | let positive_poly = poly[n..].iter(); 465 | multiexp( 466 | srs.g_negative_x[1..(negative_poly.len() + 1)].iter().chain_ext( 467 | srs.g_positive_x[0..positive_poly.len()].iter() 468 | ), 469 | negative_poly.chain_ext(positive_poly) 470 | ).into_affine() 471 | }; 472 | 473 | Aggregate { 474 | // Commitment to s(z, Y) 475 | c, 476 | // We have to open each of the S commitments to a random point `z` 477 | s_opening, 478 | // We have to open C to each constituent `y` 479 | c_openings, 480 | // Then we have to finally open C 481 | opening, 482 | } 483 | } 484 | 485 | pub fn create_advice, S: SynthesisDriver>( 486 | circuit: &C, 487 | proof: &Proof, 488 | srs: &SRS 489 | ) -> SxyAdvice 490 | { 491 | // annoying, but we need n to compute s(z, y), and this isn't 492 | // precomputed anywhere yet 493 | let n = { 494 | struct CountN { 495 | n: usize 496 | } 497 | 498 | impl<'a, E: Engine> Backend for &'a mut CountN { 499 | type LinearConstraintIndex = (); 500 | 501 | fn new_linear_constraint(&mut self) -> Self::LinearConstraintIndex { () } 502 | 503 | fn get_for_q(&self, q: usize) -> Self::LinearConstraintIndex { () } 504 | 505 | fn new_multiplication_gate(&mut self) { 506 | self.n += 1; 507 | } 508 | } 509 | 510 | let mut tmp = CountN{n:0}; 511 | S::synthesize(&mut tmp, circuit).unwrap(); // TODO 512 | 513 | tmp.n 514 | }; 515 | 516 | let z: E::Fr; 517 | let y: E::Fr; 518 | { 519 | let mut transcript = Transcript::new(&[]); 520 | transcript.commit_point(&proof.r); 521 | y = transcript.get_challenge_scalar(); 522 | transcript.commit_point(&proof.t); 523 | z = transcript.get_challenge_scalar(); 524 | } 525 | let z_inv = z.inverse().unwrap(); // TODO 526 | 527 | let (s_poly_negative, s_poly_positive) = { 528 | let mut tmp = SxEval::new(y, n); 529 | S::synthesize(&mut tmp, circuit).unwrap(); // TODO 530 | 531 | tmp.poly() 532 | }; 533 | 534 | // Compute S commitment 535 | let s = multiexp( 536 | srs.g_positive_x_alpha[0..(2 * n)] 537 | .iter() 538 | .chain_ext(srs.g_negative_x_alpha[0..(n)].iter()), 539 | s_poly_positive.iter().chain_ext(s_poly_negative.iter()) 540 | ).into_affine(); 541 | 542 | // Compute s(z, y) 543 | let mut szy = E::Fr::zero(); 544 | { 545 | let mut tmp = z; 546 | for &p in &s_poly_positive { 547 | let mut p = p; 548 | p.mul_assign(&tmp); 549 | szy.add_assign(&p); 550 | tmp.mul_assign(&z); 551 | } 552 | let mut tmp = z_inv; 553 | for &p in &s_poly_negative { 554 | let mut p = p; 555 | p.mul_assign(&tmp); 556 | szy.add_assign(&p); 557 | tmp.mul_assign(&z_inv); 558 | } 559 | } 560 | 561 | // Compute kate opening 562 | let opening = { 563 | let mut open = szy; 564 | open.negate(); 565 | 566 | let poly = kate_divison( 567 | s_poly_negative.iter().rev().chain_ext(Some(open).iter()).chain_ext(s_poly_positive.iter()), 568 | z, 569 | ); 570 | 571 | let negative_poly = poly[0..n].iter().rev(); 572 | let positive_poly = poly[n..].iter(); 573 | multiexp( 574 | srs.g_negative_x[1..(negative_poly.len() + 1)].iter().chain_ext( 575 | srs.g_positive_x[0..positive_poly.len()].iter() 576 | ), 577 | negative_poly.chain_ext(positive_poly) 578 | ).into_affine() 579 | }; 580 | 581 | SxyAdvice { 582 | s, 583 | szy, 584 | opening 585 | } 586 | } 587 | 588 | pub fn create_proof, S: SynthesisDriver>( 589 | circuit: &C, 590 | srs: &SRS 591 | ) -> Result, SynthesisError> 592 | { 593 | struct Wires { 594 | a: Vec, 595 | b: Vec, 596 | c: Vec 597 | } 598 | 599 | impl<'a, E: Engine> Backend for &'a mut Wires { 600 | type LinearConstraintIndex = (); 601 | 602 | fn new_linear_constraint(&mut self) -> Self::LinearConstraintIndex { () } 603 | 604 | fn get_for_q(&self, q: usize) -> Self::LinearConstraintIndex { 605 | () 606 | } 607 | 608 | fn new_multiplication_gate(&mut self) { 609 | self.a.push(E::Fr::zero()); 610 | self.b.push(E::Fr::zero()); 611 | self.c.push(E::Fr::zero()); 612 | } 613 | 614 | fn get_var(&self, variable: Variable) -> Option { 615 | Some(match variable { 616 | Variable::A(index) => { 617 | self.a[index - 1] 618 | }, 619 | Variable::B(index) => { 620 | self.b[index - 1] 621 | }, 622 | Variable::C(index) => { 623 | self.c[index - 1] 624 | } 625 | }) 626 | } 627 | 628 | fn set_var(&mut self, variable: Variable, value: F) -> Result<(), SynthesisError> 629 | where F: FnOnce() -> Result 630 | { 631 | let value = value()?; 632 | 633 | match variable { 634 | Variable::A(index) => { 635 | self.a[index - 1] = value; 636 | }, 637 | Variable::B(index) => { 638 | self.b[index - 1] = value; 639 | }, 640 | Variable::C(index) => { 641 | self.c[index - 1] = value; 642 | } 643 | } 644 | 645 | Ok(()) 646 | } 647 | } 648 | 649 | let mut wires = Wires { 650 | a: vec![], 651 | b: vec![], 652 | c: vec![], 653 | }; 654 | 655 | S::synthesize(&mut wires, circuit)?; 656 | 657 | let n = wires.a.len(); 658 | 659 | let mut transcript = Transcript::new(&[]); 660 | 661 | let r = multiexp( 662 | srs.g_positive_x_alpha[(srs.d - 3*n - 1)..].iter(), 663 | wires.c.iter().rev() 664 | .chain_ext(wires.b.iter().rev()) 665 | .chain_ext(Some(E::Fr::zero()).iter()) 666 | .chain_ext(wires.a.iter()), 667 | ).into_affine(); 668 | 669 | transcript.commit_point(&r); 670 | 671 | let y: E::Fr = transcript.get_challenge_scalar(); 672 | 673 | let mut rx1 = wires.b; 674 | rx1.extend(wires.c); 675 | rx1.reverse(); 676 | rx1.push(E::Fr::zero()); 677 | rx1.extend(wires.a); 678 | 679 | let mut rxy = rx1.clone(); 680 | let y_inv = y.inverse().unwrap(); // TODO 681 | let mut tmp = y.pow(&[n as u64]); 682 | 683 | for rxy in rxy.iter_mut().rev() { 684 | rxy.mul_assign(&tmp); 685 | tmp.mul_assign(&y_inv); 686 | } 687 | 688 | let (s_poly_negative, s_poly_positive) = { 689 | let mut tmp = SxEval::new(y, n); 690 | S::synthesize(&mut tmp, circuit).unwrap(); // TODO 691 | 692 | tmp.poly() 693 | }; 694 | 695 | let mut rxy_prime = rxy.clone(); 696 | { 697 | rxy_prime.resize(4 * n + 1, E::Fr::zero()); 698 | // Add s(x, y) 699 | for (r, s) in rxy_prime[0..(2 * n)] 700 | .iter_mut() 701 | .rev() 702 | .zip(s_poly_negative) 703 | { 704 | r.add_assign(&s); 705 | } 706 | for (r, s) in rxy_prime[(2 * n + 1)..].iter_mut().zip(s_poly_positive) { 707 | r.add_assign(&s); 708 | } 709 | } 710 | 711 | let mut txy = multiply_polynomials::(rx1.clone(), rxy_prime); 712 | txy[4 * n] = E::Fr::zero(); // -k(y) 713 | 714 | let t = multiexp( 715 | srs.g_positive_x_alpha[0..(3 * n)] 716 | .iter() 717 | .chain_ext(srs.g_negative_x_alpha[0..(4 * n)].iter()), 718 | txy[(4 * n + 1)..] 719 | .iter() 720 | .chain_ext(txy[0..4 * n].iter().rev()), 721 | ).into_affine(); 722 | 723 | transcript.commit_point(&t); 724 | 725 | let z: E::Fr = transcript.get_challenge_scalar(); 726 | let z_inv = z.inverse().unwrap(); // TODO 727 | 728 | // TODO: use the faster way to evaluate the polynomials 729 | let mut rz = E::Fr::zero(); 730 | { 731 | let mut tmp = z.pow(&[n as u64]); 732 | 733 | for coeff in rx1.iter().rev() { 734 | let mut coeff = *coeff; 735 | coeff.mul_assign(&tmp); 736 | rz.add_assign(&coeff); 737 | tmp.mul_assign(&z_inv); 738 | } 739 | } 740 | 741 | let mut rzy = E::Fr::zero(); 742 | { 743 | let mut tmp = z.pow(&[n as u64]); 744 | 745 | for mut coeff in rxy.into_iter().rev() { 746 | coeff.mul_assign(&tmp); 747 | rzy.add_assign(&coeff); 748 | tmp.mul_assign(&z_inv); 749 | } 750 | } 751 | 752 | transcript.commit_scalar(&rz); 753 | transcript.commit_scalar(&rzy); 754 | 755 | let r1: E::Fr = transcript.get_challenge_scalar(); 756 | 757 | let zy_opening = { 758 | // r(X, 1) - r(z, y) 759 | rx1[2 * n].sub_assign(&rzy); 760 | 761 | let mut point = y; 762 | point.mul_assign(&z); 763 | let poly = kate_divison( 764 | rx1.iter(), 765 | point, 766 | ); 767 | 768 | let negative_poly = poly[0..2*n].iter().rev(); 769 | let positive_poly = poly[2*n..].iter(); 770 | multiexp( 771 | srs.g_negative_x[1..(negative_poly.len() + 1)].iter().chain_ext( 772 | srs.g_positive_x[0..positive_poly.len()].iter() 773 | ), 774 | negative_poly.chain_ext(positive_poly) 775 | ).into_affine() 776 | }; 777 | 778 | let z_opening = { 779 | rx1[2 * n].add_assign(&rzy); // restore 780 | 781 | for (t, &r) in txy[2 * n..].iter_mut().zip(rx1.iter()) { 782 | let mut r = r; 783 | r.mul_assign(&r1); 784 | t.add_assign(&r); 785 | } 786 | 787 | let mut val = E::Fr::zero(); 788 | { 789 | assert_eq!(txy.len(), 3*n + 1 + 4*n); 790 | let mut tmp = z.pow(&[(3*n) as u64]); 791 | 792 | for coeff in txy.iter().rev() { 793 | let mut coeff = *coeff; 794 | coeff.mul_assign(&tmp); 795 | val.add_assign(&coeff); 796 | tmp.mul_assign(&z_inv); 797 | } 798 | } 799 | 800 | txy[4 * n].sub_assign(&val); 801 | 802 | let poly = kate_divison( 803 | txy.iter(), 804 | z, 805 | ); 806 | 807 | let negative_poly = poly[0..4*n].iter().rev(); 808 | let positive_poly = poly[4*n..].iter(); 809 | multiexp( 810 | srs.g_negative_x[1..(negative_poly.len() + 1)].iter().chain_ext( 811 | srs.g_positive_x[0..positive_poly.len()].iter() 812 | ), 813 | negative_poly.chain_ext(positive_poly) 814 | ).into_affine() 815 | }; 816 | 817 | Ok(Proof { 818 | r, rz, rzy, t, z_opening, zy_opening 819 | }) 820 | } 821 | 822 | 823 | /* 824 | s(X, Y) = \sum\limits_{i=1}^N \sum\limits_{q=1}^Q Y^{q+N} u_{i,q} X^{-i} 825 | + \sum\limits_{i=1}^N \sum\limits_{q=1}^Q Y^{q+N} v_{i,q} X^{i} 826 | + \sum\limits_{i=1}^N \sum\limits_{q=1}^Q Y^{q+N} w_{i,q} X^{i+N} 827 | - \sum\limits_{i=1}^N Y^i X^{i+N} 828 | - \sum\limits_{i=1}^N Y^{-i} X^{i+N} 829 | */ 830 | struct SyEval { 831 | max_n: usize, 832 | current_q: usize, 833 | 834 | // x^{-1}, ..., x^{-N} 835 | a: Vec, 836 | 837 | // x^1, ..., x^{N} 838 | b: Vec, 839 | 840 | // x^{N+1}, ..., x^{2*N} 841 | c: Vec, 842 | 843 | // coeffs for y^1, ..., y^{N+Q} 844 | positive_coeffs: Vec, 845 | 846 | // coeffs for y^{-1}, y^{-2}, ..., y^{-N} 847 | negative_coeffs: Vec, 848 | } 849 | 850 | 851 | impl SyEval { 852 | fn new(x: E::Fr, n: usize, q: usize) -> Self { 853 | let xinv = x.inverse().unwrap(); 854 | let mut tmp = E::Fr::one(); 855 | let mut a = vec![E::Fr::zero(); n]; 856 | for a in &mut a { 857 | tmp.mul_assign(&xinv); // tmp = x^{-i} 858 | *a = tmp; 859 | } 860 | 861 | let mut tmp = E::Fr::one(); 862 | let mut b = vec![E::Fr::zero(); n]; 863 | for b in &mut b { 864 | tmp.mul_assign(&x); // tmp = x^{i} 865 | *b = tmp; 866 | } 867 | 868 | let mut positive_coeffs = vec![E::Fr::zero(); n + q]; 869 | let mut negative_coeffs = vec![E::Fr::zero(); n]; 870 | 871 | let mut c = vec![E::Fr::zero(); n]; 872 | for ((c, positive_coeff), negative_coeff) in c.iter_mut().zip(&mut positive_coeffs).zip(&mut negative_coeffs) { 873 | tmp.mul_assign(&x); // tmp = x^{i+N} 874 | *c = tmp; 875 | 876 | // - \sum\limits_{i=1}^N Y^i X^{i+N} 877 | let mut tmp = tmp; 878 | tmp.negate(); 879 | *positive_coeff = tmp; 880 | 881 | // - \sum\limits_{i=1}^N Y^{-i} X^{i+N} 882 | *negative_coeff = tmp; 883 | } 884 | 885 | SyEval { 886 | a, 887 | b, 888 | c, 889 | positive_coeffs, 890 | negative_coeffs, 891 | current_q: 0, 892 | max_n: n, 893 | } 894 | } 895 | 896 | fn poly(self) -> (Vec, Vec) { 897 | (self.negative_coeffs, self.positive_coeffs) 898 | } 899 | 900 | fn finalize(self, y: E::Fr) -> E::Fr { 901 | let mut acc = E::Fr::zero(); 902 | 903 | let mut tmp = y; 904 | for mut coeff in self.positive_coeffs { 905 | coeff.mul_assign(&tmp); 906 | acc.add_assign(&coeff); 907 | tmp.mul_assign(&y); 908 | } 909 | let yinv = y.inverse().unwrap(); // TODO 910 | let mut tmp = yinv; 911 | for mut coeff in self.negative_coeffs { 912 | coeff.mul_assign(&tmp); 913 | acc.add_assign(&coeff); 914 | tmp.mul_assign(&yinv); 915 | } 916 | 917 | acc 918 | } 919 | } 920 | 921 | impl<'a, E: Engine> Backend for &'a mut SyEval { 922 | type LinearConstraintIndex = usize; 923 | 924 | fn new_linear_constraint(&mut self) -> usize { 925 | self.current_q += 1; 926 | self.current_q 927 | } 928 | 929 | fn get_for_q(&self, q: usize) -> Self::LinearConstraintIndex { 930 | q 931 | } 932 | 933 | fn insert_coefficient(&mut self, var: Variable, coeff: Coeff, q: &usize) { 934 | match var { 935 | Variable::A(index) => { 936 | let index = index - 1; 937 | // Y^{q+N} += X^{-i} * coeff 938 | let mut tmp = self.a[index]; 939 | coeff.multiply(&mut tmp); 940 | let yindex = *q + self.max_n; 941 | self.positive_coeffs[yindex - 1].add_assign(&tmp); 942 | } 943 | Variable::B(index) => { 944 | let index = index - 1; 945 | // Y^{q+N} += X^{i} * coeff 946 | let mut tmp = self.b[index]; 947 | coeff.multiply(&mut tmp); 948 | let yindex = *q + self.max_n; 949 | self.positive_coeffs[yindex - 1].add_assign(&tmp); 950 | } 951 | Variable::C(index) => { 952 | let index = index - 1; 953 | // Y^{q+N} += X^{i+N} * coeff 954 | let mut tmp = self.c[index]; 955 | coeff.multiply(&mut tmp); 956 | let yindex = *q + self.max_n; 957 | self.positive_coeffs[yindex - 1].add_assign(&tmp); 958 | } 959 | }; 960 | } 961 | } 962 | 963 | /* 964 | s(X, Y) = \sum\limits_{i=1}^N u_i(Y) X^{-i} 965 | + \sum\limits_{i=1}^N v_i(Y) X^{i} 966 | + \sum\limits_{i=1}^N w_i(Y) X^{i+N} 967 | 968 | where 969 | 970 | u_i(Y) = \sum\limits_{q=1}^Q Y^{q+N} u_{i,q} 971 | v_i(Y) = \sum\limits_{q=1}^Q Y^{q+N} v_{i,q} 972 | w_i(Y) = -Y^i + -Y^{-i} + \sum\limits_{q=1}^Q Y^{q+N} w_{i,q} 973 | 974 | */ 975 | #[derive(Clone)] 976 | struct SxEval { 977 | y: E::Fr, 978 | 979 | // current value of y^{q+N} 980 | yqn: E::Fr, 981 | 982 | // x^{-i} (\sum\limits_{q=1}^Q y^{q+N} u_{q,i}) 983 | u: Vec, 984 | // x^{i} (\sum\limits_{q=1}^Q y^{q+N} v_{q,i}) 985 | v: Vec, 986 | // x^{i+N} (-y^i -y^{-i} + \sum\limits_{q=1}^Q y^{q+N} w_{q,i}) 987 | w: Vec, 988 | 989 | max_n: usize, 990 | } 991 | 992 | impl SxEval { 993 | fn new(y: E::Fr, n: usize) -> Self { 994 | let y_inv = y.inverse().unwrap(); // TODO 995 | 996 | let yqn = y.pow(&[n as u64]); 997 | 998 | let u = vec![E::Fr::zero(); n]; 999 | let v = vec![E::Fr::zero(); n]; 1000 | let mut w = vec![E::Fr::zero(); n]; 1001 | 1002 | let mut tmp1 = y; 1003 | let mut tmp2 = y_inv; 1004 | for w in &mut w { 1005 | let mut new = tmp1; 1006 | new.add_assign(&tmp2); 1007 | new.negate(); 1008 | *w = new; 1009 | tmp1.mul_assign(&y); 1010 | tmp2.mul_assign(&y_inv); 1011 | } 1012 | 1013 | SxEval { 1014 | y, 1015 | yqn, 1016 | u, 1017 | v, 1018 | w, 1019 | 1020 | max_n: n 1021 | } 1022 | } 1023 | 1024 | fn poly(mut self) -> (Vec, Vec) { 1025 | self.v.extend(self.w); 1026 | 1027 | (self.u, self.v) 1028 | } 1029 | 1030 | fn finalize(self, x: E::Fr) -> E::Fr { 1031 | let x_inv = x.inverse().unwrap(); // TODO 1032 | 1033 | let mut tmp = x_inv; 1034 | 1035 | let mut acc = E::Fr::zero(); 1036 | for mut u in self.u { 1037 | u.mul_assign(&tmp); 1038 | acc.add_assign(&u); 1039 | tmp.mul_assign(&x_inv); 1040 | } 1041 | 1042 | let mut tmp = x; 1043 | for mut v in self.v { 1044 | v.mul_assign(&tmp); 1045 | acc.add_assign(&v); 1046 | tmp.mul_assign(&x); 1047 | } 1048 | for mut w in self.w { 1049 | w.mul_assign(&tmp); 1050 | acc.add_assign(&w); 1051 | tmp.mul_assign(&x); 1052 | } 1053 | 1054 | acc 1055 | } 1056 | } 1057 | 1058 | impl<'a, E: Engine> Backend for &'a mut SxEval { 1059 | type LinearConstraintIndex = E::Fr; 1060 | 1061 | fn new_linear_constraint(&mut self) -> E::Fr { 1062 | self.yqn.mul_assign(&self.y); 1063 | self.yqn 1064 | } 1065 | 1066 | fn get_for_q(&self, q: usize) -> Self::LinearConstraintIndex { 1067 | self.y.pow(&[(self.max_n + q) as u64]) 1068 | } 1069 | 1070 | fn insert_coefficient(&mut self, var: Variable, coeff: Coeff, y: &E::Fr) { 1071 | let acc = match var { 1072 | Variable::A(index) => { 1073 | &mut self.u[index - 1] 1074 | } 1075 | Variable::B(index) => { 1076 | &mut self.v[index - 1] 1077 | } 1078 | Variable::C(index) => { 1079 | &mut self.w[index - 1] 1080 | } 1081 | }; 1082 | 1083 | match coeff { 1084 | Coeff::Zero => { }, 1085 | Coeff::One => { 1086 | acc.add_assign(&y); 1087 | }, 1088 | Coeff::NegativeOne => { 1089 | acc.sub_assign(&y); 1090 | }, 1091 | Coeff::Full(mut val) => { 1092 | val.mul_assign(&y); 1093 | acc.add_assign(&val); 1094 | } 1095 | } 1096 | } 1097 | } 1098 | 1099 | #[test] 1100 | fn my_fun_circuit_test() { 1101 | use pairing::bls12_381::{Bls12, Fr}; 1102 | use pairing::PrimeField; 1103 | use super::*; 1104 | use crate::synthesis::*; 1105 | 1106 | struct MyCircuit; 1107 | 1108 | impl Circuit for MyCircuit { 1109 | fn synthesize>(&self, cs: &mut CS) -> Result<(), SynthesisError> { 1110 | let (a, b, _) = cs.multiply(|| { 1111 | Ok(( 1112 | E::Fr::from_str("10").unwrap(), 1113 | E::Fr::from_str("20").unwrap(), 1114 | E::Fr::from_str("200").unwrap(), 1115 | )) 1116 | })?; 1117 | 1118 | cs.enforce_zero(LinearCombination::from(a) + a - b); 1119 | 1120 | //let multiplier = cs.alloc_input(|| Ok(E::Fr::from_str("20").unwrap()))?; 1121 | 1122 | //cs.enforce_zero(LinearCombination::from(b) - multiplier); 1123 | 1124 | Ok(()) 1125 | } 1126 | } 1127 | 1128 | let srs = SRS::::new( 1129 | 20, 1130 | Fr::from_str("22222").unwrap(), 1131 | Fr::from_str("33333333").unwrap(), 1132 | ); 1133 | let proof = create_proof::(&MyCircuit, &srs).unwrap(); 1134 | 1135 | use std::time::{Instant}; 1136 | let start = Instant::now(); 1137 | let mut batch = MultiVerifier::::new(MyCircuit, &srs).unwrap(); 1138 | 1139 | for _ in 0..1 { 1140 | batch.add_proof(&proof, &[/*Fr::from_str("20").unwrap()*/], |_, _| None); 1141 | } 1142 | 1143 | assert!(batch.check_all()); 1144 | 1145 | let elapsed = start.elapsed(); 1146 | println!("time to verify: {:?}", elapsed); 1147 | } 1148 | -------------------------------------------------------------------------------- /src/srs.rs: -------------------------------------------------------------------------------- 1 | use pairing::{CurveAffine, CurveProjective, Engine, Field, PrimeField, Wnaf}; 2 | 3 | pub struct SRS { 4 | pub d: usize, 5 | 6 | // g^{x^0}, g^{x^{-1}}, g^{x^{-2}}, ..., g^{x^{-d}} 7 | pub g_negative_x: Vec, 8 | 9 | // g^{x^0}, g^{x^{1}}, g^{x^{2}}, ..., g^{x^{d}} 10 | pub g_positive_x: Vec, 11 | 12 | // g^{x^0}, g^{x^{-1}}, g^{x^{-2}}, ..., g^{x^{-d}} 13 | pub h_negative_x: Vec, 14 | 15 | // g^{x^0}, g^{x^{1}}, g^{x^{2}}, ..., g^{x^{d}} 16 | pub h_positive_x: Vec, 17 | 18 | // alpha*(g^{x^{-1}}, g^{x^{-2}}, ..., g^{x^{-d}}) 19 | pub g_negative_x_alpha: Vec, 20 | 21 | // alpha*(g^{x^{1}}, g^{x^{2}}, ..., g^{x^{d}}) 22 | pub g_positive_x_alpha: Vec, 23 | 24 | // alpha*(h^{x^0}, h^{x^{-1}}, g^{x^{-2}}, ..., g^{x^{-d}}) 25 | pub h_negative_x_alpha: Vec, 26 | 27 | // alpha*(h^{x^0}, g^{x^{1}}, g^{x^{2}}, ..., g^{x^{d}}) 28 | pub h_positive_x_alpha: Vec, 29 | } 30 | 31 | impl SRS { 32 | pub fn dummy(d: usize, _: E::Fr, _: E::Fr) -> Self { 33 | SRS { 34 | d: d, 35 | g_negative_x: vec![E::G1Affine::one(); d + 1], 36 | g_positive_x: vec![E::G1Affine::one(); d + 1], 37 | 38 | h_negative_x: vec![E::G2Affine::one(); d + 1], 39 | h_positive_x: vec![E::G2Affine::one(); d + 1], 40 | 41 | g_negative_x_alpha: vec![E::G1Affine::one(); d], 42 | g_positive_x_alpha: vec![E::G1Affine::one(); d], 43 | 44 | h_negative_x_alpha: vec![E::G2Affine::one(); d + 1], 45 | h_positive_x_alpha: vec![E::G2Affine::one(); d + 1], 46 | } 47 | } 48 | 49 | pub fn new(d: usize, x: E::Fr, alpha: E::Fr) -> Self { 50 | let mut g1 = Wnaf::new(); 51 | let mut g1 = g1.base(E::G1::one(), d * 4); 52 | let mut g2 = Wnaf::new(); 53 | let mut g2 = g2.base(E::G2::one(), d * 4); 54 | 55 | fn table( 56 | mut cur: C::Scalar, 57 | step: C::Scalar, 58 | num: usize, 59 | table: &mut Wnaf>, 60 | ) -> Vec { 61 | let mut v = vec![]; 62 | for _ in 0..num { 63 | v.push(table.scalar(cur.into_repr())); 64 | cur.mul_assign(&step); 65 | } 66 | C::Projective::batch_normalization(&mut v); 67 | let v = v.into_iter().map(|e| e.into_affine()).collect(); 68 | v 69 | } 70 | 71 | let x_inv = x.inverse().unwrap(); 72 | 73 | let mut x_alpha = x; 74 | x_alpha.mul_assign(&alpha); 75 | 76 | let mut inv_x_alpha = x_inv; 77 | inv_x_alpha.mul_assign(&alpha); 78 | 79 | SRS { 80 | d: d, 81 | g_negative_x: table(E::Fr::one(), x_inv, d + 1, &mut g1), 82 | g_positive_x: table(E::Fr::one(), x, d + 1, &mut g1), 83 | 84 | h_negative_x: table(E::Fr::one(), x_inv, d + 1, &mut g2), 85 | h_positive_x: table(E::Fr::one(), x, d + 1, &mut g2), 86 | 87 | g_negative_x_alpha: table(inv_x_alpha, x_inv, d, &mut g1), 88 | g_positive_x_alpha: table(x_alpha, x, d, &mut g1), 89 | 90 | h_negative_x_alpha: table(alpha, x_inv, d + 1, &mut g2), 91 | h_positive_x_alpha: table(alpha, x, d + 1, &mut g2), 92 | } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/synthesis.rs: -------------------------------------------------------------------------------- 1 | use pairing::{Engine, Field}; 2 | use crate::{ConstraintSystem, SynthesisError, Variable, LinearCombination, Coeff, Circuit}; 3 | use std::marker::PhantomData; 4 | use std::iter::Peekable; 5 | 6 | /// This is a backend for the `SynthesisDriver` to relay information about 7 | /// the concrete circuit. One backend might just collect basic information 8 | /// about the circuit for verification, while another actually constructs 9 | /// a witness. 10 | pub trait Backend { 11 | type LinearConstraintIndex; 12 | 13 | /// Get the value of a variable. Can return None if we don't know. 14 | fn get_var(&self, _variable: Variable) -> Option { None } 15 | 16 | /// Set the value of a variable. Might error if this backend expects to know it. 17 | fn set_var(&mut self, _variable: Variable, _value: F) -> Result<(), SynthesisError> 18 | where F: FnOnce() -> Result { Ok(()) } 19 | 20 | /// Create a new multiplication gate. 21 | fn new_multiplication_gate(&mut self) { } 22 | 23 | /// Create a new linear constraint, returning the power of Y for caching purposes. 24 | fn new_linear_constraint(&mut self) -> Self::LinearConstraintIndex; 25 | 26 | /// Insert a term into a linear constraint. TODO: bad name of function 27 | fn insert_coefficient(&mut self, _var: Variable, _coeff: Coeff, y: &Self::LinearConstraintIndex) { } 28 | 29 | /// Compute a `LinearConstraintIndex` from `q`. 30 | fn get_for_q(&self, q: usize) -> Self::LinearConstraintIndex; 31 | 32 | /// Mark y^{_index} as the power of y cooresponding to the public input 33 | /// coefficient for the next public input, in the k(Y) polynomial. 34 | fn new_k_power(&mut self, _index: usize) { } 35 | } 36 | 37 | /// This is an abstraction which synthesizes circuits. 38 | pub trait SynthesisDriver { 39 | fn synthesize, B: Backend>(backend: B, circuit: &C) -> Result<(), SynthesisError>; 40 | } 41 | 42 | pub struct Basic; 43 | 44 | impl SynthesisDriver for Basic { 45 | fn synthesize, B: Backend>(backend: B, circuit: &C) -> Result<(), SynthesisError> { 46 | struct Synthesizer> { 47 | backend: B, 48 | current_variable: Option, 49 | _marker: PhantomData, 50 | q: usize, 51 | n: usize, 52 | } 53 | 54 | impl> ConstraintSystem for Synthesizer { 55 | const ONE: Variable = Variable::A(1); 56 | 57 | fn alloc(&mut self, value: F) -> Result 58 | where 59 | F: FnOnce() -> Result 60 | { 61 | match self.current_variable.take() { 62 | Some(index) => { 63 | let var_a = Variable::A(index); 64 | let var_b = Variable::B(index); 65 | let var_c = Variable::C(index); 66 | 67 | let mut product = None; 68 | 69 | let value_a = self.backend.get_var(var_a); 70 | 71 | self.backend.set_var(var_b, || { 72 | let value_b = value()?; 73 | product = Some(value_a.ok_or(SynthesisError::AssignmentMissing)?); 74 | product.as_mut().map(|product| product.mul_assign(&value_b)); 75 | 76 | Ok(value_b) 77 | })?; 78 | 79 | self.backend.set_var(var_c, || { 80 | product.ok_or(SynthesisError::AssignmentMissing) 81 | })?; 82 | 83 | self.current_variable = None; 84 | 85 | Ok(var_b) 86 | }, 87 | None => { 88 | self.n += 1; 89 | let index = self.n; 90 | self.backend.new_multiplication_gate(); 91 | 92 | let var_a = Variable::A(index); 93 | 94 | self.backend.set_var(var_a, value)?; 95 | 96 | self.current_variable = Some(index); 97 | 98 | Ok(var_a) 99 | } 100 | } 101 | } 102 | 103 | fn alloc_input(&mut self, value: F) -> Result 104 | where 105 | F: FnOnce() -> Result 106 | { 107 | let input_var = self.alloc(value)?; 108 | 109 | self.enforce_zero(LinearCombination::zero() + input_var); 110 | self.backend.new_k_power(self.q); 111 | 112 | Ok(input_var) 113 | } 114 | 115 | fn enforce_zero(&mut self, lc: LinearCombination) 116 | { 117 | self.q += 1; 118 | let y = self.backend.new_linear_constraint(); 119 | 120 | for (var, coeff) in lc.as_ref() { 121 | self.backend.insert_coefficient(*var, *coeff, &y); 122 | } 123 | } 124 | 125 | fn multiply(&mut self, values: F) -> Result<(Variable, Variable, Variable), SynthesisError> 126 | where 127 | F: FnOnce() -> Result<(E::Fr, E::Fr, E::Fr), SynthesisError> 128 | { 129 | self.n += 1; 130 | let index = self.n; 131 | self.backend.new_multiplication_gate(); 132 | 133 | let a = Variable::A(index); 134 | let b = Variable::B(index); 135 | let c = Variable::C(index); 136 | 137 | let mut b_val = None; 138 | let mut c_val = None; 139 | 140 | self.backend.set_var(a, || { 141 | let (a, b, c) = values()?; 142 | 143 | b_val = Some(b); 144 | c_val = Some(c); 145 | 146 | Ok(a) 147 | })?; 148 | 149 | self.backend.set_var(b, || { 150 | b_val.ok_or(SynthesisError::AssignmentMissing) 151 | })?; 152 | 153 | self.backend.set_var(c, || { 154 | c_val.ok_or(SynthesisError::AssignmentMissing) 155 | })?; 156 | 157 | Ok((a, b, c)) 158 | } 159 | 160 | fn get_value(&self, var: Variable) -> Result { 161 | self.backend.get_var(var).ok_or(()) 162 | } 163 | } 164 | 165 | let mut tmp: Synthesizer = Synthesizer { 166 | backend: backend, 167 | current_variable: None, 168 | _marker: PhantomData, 169 | q: 0, 170 | n: 0, 171 | }; 172 | 173 | let one = tmp.alloc_input(|| Ok(E::Fr::one())).expect("should have no issues"); 174 | 175 | match (one, as ConstraintSystem>::ONE) { 176 | (Variable::A(1), Variable::A(1)) => {}, 177 | _ => panic!("one variable is incorrect") 178 | } 179 | 180 | circuit.synthesize(&mut tmp)?; 181 | 182 | // TODO: add blinding factors so we actually get zero-knowledge 183 | 184 | // println!("n = {}", tmp.n); 185 | 186 | Ok(()) 187 | } 188 | } 189 | 190 | /* 191 | 192 | In order to use the fully succinct version of Sonic, the resulting s(X, Y) polynomial 193 | must be in a more "trivial" form 194 | 195 | s(X, Y) = X^{-N - 1} Y^N s_1(X, Y) - X^N s_2(X, Y) 196 | 197 | where 198 | 199 | s_1(X, Y) = \sum\limits_{i=1}^N u'_i(Y) X^{-i + N + 1} 200 | + \sum\limits_{i=1}^N v'_i(Y) X^{i + N + 1} 201 | + \sum\limits_{i=1}^N w'_i(Y) X^{i + 2N + 1} 202 | s_2(X, Y) = \sum\limits_{i=1}^N (Y^i + Y^{-i}) X^i 203 | 204 | u'_i(Y) = \sum\limits_{q=1}^Q Y^q u_{q,i} 205 | v'_i(Y) = \sum\limits_{q=1}^Q Y^q v_{q,i} 206 | w'_i(Y) = \sum\limits_{q=1}^Q Y^q w_{q,i} 207 | 208 | such that s_1(X, Y) can be expressed as the sum of M permutation polynomials. 209 | 210 | It is trivial for the verifier to evaluate s_2(X, Y), since polynomials of the form 211 | x + x^2 + x^3 + ... can be evaluated with a logarithmic number of field operations. 212 | 213 | In order to get s_1(X, Y) into the form needed, each constituent permutation polynomial 214 | is effectively of the form 215 | 216 | s_j(X, Y) = \sum\limits_{i=1}^{3N+1} c_i X^i Y^\sigma_j(i) 217 | 218 | where \sigma_j(i) defines the permutation. The X^i corresponds to the wire, and the 219 | Y^\sigma_j(i) corresponds to the index of the linear constraint. 220 | 221 | This effectively means that within each polynomial there can be only one particular 222 | X^i term, and so wires can only appear in M different linear combinations. Further, 223 | because there is only ever a particular Y^i term in each M permutation polynomial, 224 | linear combinations can have only M wires. 225 | 226 | In order to synthesize a constraint system into a form that supports this wonky 227 | arrangement, we need M>=3. The general goal is to treat each permutation polynomial 228 | as a "slot" and, when constructing linear constraints, keep track of which slots are 229 | "occupied" by wires, either with respect to the wires themselves or with respect to 230 | the linear combination as it is being assembled. 231 | 232 | If the linear combination has more than M terms, then we need to recursively 233 | construct ephemeral wires to hold the values of the remaining terms, and relate those 234 | wires to those terms in new linear combinations. 235 | 236 | Once our linear combinations are small enough to fit the terms into the M slots, 237 | we eagerly shove the terms in. The easy case is when a slot is available for both 238 | the wire and the linear combination. The remaining cases can be addressed generally 239 | by imagining that the wire has no available slots. We will create a new ephemeral 240 | wire that holds the same value as the original wire and use this wire to insert the 241 | linear combination. Then, we'll swap one of the terms from another slot into the new 242 | ephemeral wire, freeing a slot in the original wire. Then, we trivially have that the 243 | new wire and old wire have distinct slots free (since M>=3) and so we can now force 244 | that they become equal. 245 | 246 | In terms of actually implementing this, things can get tricky. We don't want to end 247 | up in a circumstance where we are infinitely recursing, which can happen depending on 248 | the order we create linear combinations for the ephemeral variables. 249 | */ 250 | pub struct Permutation3; 251 | 252 | const M: usize = 3; 253 | 254 | impl SynthesisDriver for Permutation3 { 255 | fn synthesize, B: Backend>(backend: B, circuit: &C) -> Result<(), SynthesisError> { 256 | struct Synthesizer> { 257 | backend: B, 258 | current_variable: Option, 259 | _marker: PhantomData, 260 | q: usize, 261 | n: usize, 262 | 263 | // These vectors will hold, for all of the wires, the terms related to these 264 | // wires for each of the M permutation polynomials. The Coeff is the 265 | // coefficient, and the usize is q, the index of the linear constraint and is 266 | // related to the power of Y in the s_1(X, Y) polynomial. 267 | a: Vec<[Option<(Coeff, usize)>; M]>, 268 | b: Vec<[Option<(Coeff, usize)>; M]>, 269 | c: Vec<[Option<(Coeff, usize)>; M]>, 270 | } 271 | 272 | impl> ConstraintSystem for Synthesizer { 273 | const ONE: Variable = Variable::A(1); 274 | 275 | fn alloc(&mut self, value: F) -> Result 276 | where 277 | F: FnOnce() -> Result 278 | { 279 | match self.current_variable.take() { 280 | Some(index) => { 281 | let var_a = Variable::A(index); 282 | let var_b = Variable::B(index); 283 | let var_c = Variable::C(index); 284 | 285 | let mut product = None; 286 | 287 | let value_a = self.backend.get_var(var_a); 288 | 289 | self.backend.set_var(var_b, || { 290 | let value_b = value()?; 291 | product = Some(value_a.ok_or(SynthesisError::AssignmentMissing)?); 292 | product.as_mut().map(|product| product.mul_assign(&value_b)); 293 | 294 | Ok(value_b) 295 | })?; 296 | 297 | self.backend.set_var(var_c, || { 298 | product.ok_or(SynthesisError::AssignmentMissing) 299 | })?; 300 | 301 | self.current_variable = None; 302 | 303 | Ok(var_b) 304 | }, 305 | None => { 306 | self.n += 1; 307 | let index = self.n; 308 | self.backend.new_multiplication_gate(); 309 | 310 | // Create slots for the new wires. 311 | self.a.push([None; M]); 312 | self.b.push([None; M]); 313 | self.c.push([None; M]); 314 | 315 | let var_a = Variable::A(index); 316 | 317 | self.backend.set_var(var_a, value)?; 318 | 319 | self.current_variable = Some(index); 320 | 321 | Ok(var_a) 322 | } 323 | } 324 | } 325 | 326 | fn alloc_input(&mut self, value: F) -> Result 327 | where 328 | F: FnOnce() -> Result 329 | { 330 | let input_var = self.alloc(value)?; 331 | 332 | self.enforce_zero(LinearCombination::zero() + input_var); 333 | // The new variable has all free slots, so this shouldn't create 334 | // more than one linear combination. 335 | self.backend.new_k_power(self.q); 336 | 337 | Ok(input_var) 338 | } 339 | 340 | fn enforce_zero(&mut self, lc: LinearCombination) 341 | { 342 | // We just redirect things into the (recursing) enforce_equals method which 343 | // does the actual work. Annoyingly, we need to use dynamic dispatch on the 344 | // underlying iterator because once you've taken a Peekable you can't get 345 | // the underlying iterator (since .next() may have been called on it) so 346 | // at each depth of recursion we'd end up with a new type, which is 347 | // impossible for the compiler to reason about. 348 | let lc = lc.as_ref(); 349 | let lc: &mut Iterator)> = &mut lc.into_iter(); 350 | let lc = lc.peekable(); 351 | 352 | self.enforce_equals(lc, None); 353 | } 354 | 355 | fn multiply(&mut self, values: F) -> Result<(Variable, Variable, Variable), SynthesisError> 356 | where 357 | F: FnOnce() -> Result<(E::Fr, E::Fr, E::Fr), SynthesisError> 358 | { 359 | self.n += 1; 360 | let index = self.n; 361 | self.backend.new_multiplication_gate(); 362 | 363 | // Create slots for the new wires. 364 | self.a.push([None; M]); 365 | self.b.push([None; M]); 366 | self.c.push([None; M]); 367 | 368 | let a = Variable::A(index); 369 | let b = Variable::B(index); 370 | let c = Variable::C(index); 371 | 372 | let mut b_val = None; 373 | let mut c_val = None; 374 | 375 | self.backend.set_var(a, || { 376 | let (a, b, c) = values()?; 377 | 378 | b_val = Some(b); 379 | c_val = Some(c); 380 | 381 | Ok(a) 382 | })?; 383 | 384 | self.backend.set_var(b, || { 385 | b_val.ok_or(SynthesisError::AssignmentMissing) 386 | })?; 387 | 388 | self.backend.set_var(c, || { 389 | c_val.ok_or(SynthesisError::AssignmentMissing) 390 | })?; 391 | 392 | Ok((a, b, c)) 393 | } 394 | 395 | fn get_value(&self, var: Variable) -> Result { 396 | self.backend.get_var(var).ok_or(()) 397 | } 398 | } 399 | 400 | impl> Synthesizer { 401 | // Enforces that the value of `lhs` equals the value 402 | // of `rhs`, returning the value of the left hand side 403 | // as determined by the assignment. If rhs is none, it 404 | // is interpreted to be zero. 405 | fn enforce_equals<'a>( 406 | &mut self, 407 | mut lhs: Peekable<&mut Iterator)>>, 408 | rhs: Option 409 | ) -> Option 410 | { 411 | // First, let's create a new linear constraint. We'll save its y value 412 | // for the backend and q as well. 413 | self.q += 1; 414 | let q = self.q; 415 | let y = self.backend.new_linear_constraint(); 416 | let mut slots_available = [true; M]; 417 | let mut num_slots_available = M; 418 | 419 | // If the caller is enforce_equals we need to return the value of the lhs 420 | // so that rhs can be assigned properly, so we keep track of it here. 421 | let mut current_value = if rhs.is_some() { Some(E::Fr::zero()) } else { None }; 422 | 423 | // If rhs is Some, then we _need_ to involve it in this 424 | // linear constraint, so let's just handle it right away. (This also 425 | // helps avoid an infinite recursion issue described later.) 426 | if let Some(rhs) = rhs { 427 | self.emplace_variable(&mut slots_available, &y, rhs, Coeff::NegativeOne, q); 428 | num_slots_available -= 1; 429 | } 430 | 431 | // Iterate through the linear combination 432 | loop { 433 | if let Some(term) = lhs.next() { 434 | assert!(num_slots_available > 0); 435 | 436 | if num_slots_available == 1 && lhs.peek().is_some() { 437 | // We'll be out of slots if we add this variable to the linear 438 | // combination; instead, create an ephemeral variable to hold 439 | // the value of the remaining terms and use that. Temporarily, 440 | // give the variable "zero" value. 441 | let ephemeral = self.alloc(|| Ok(E::Fr::zero())).expect("assignment is provided so this should not fail"); 442 | 443 | // One of the annoying "tricks" we have to embrace is that the ephemeral 444 | // variable has all of its slots available, and so because it's the rhs 445 | // when we recursively call `enforce_equals` we know that it will not trigger 446 | // a condition in `emplace_variable` that results in the variable being 447 | // duplicated; otherwise, the duplicate variable will have a value of zero 448 | // and we'd have to somehow track all of the duplicates when we later assign. 449 | let mut iter = Some(term).into_iter().chain(lhs); 450 | let iter: &mut Iterator)> = &mut iter; 451 | let value = self.enforce_equals(iter.peekable(), Some(ephemeral)); 452 | 453 | // Set the correct ephemeral value right away 454 | self.backend.set_var(ephemeral, || { 455 | value.ok_or(SynthesisError::AssignmentMissing) 456 | }).expect("assignment is provided so this should not fail"); 457 | 458 | // Fix the underlying assignment -- the c-wire value will change if the ephemeral 459 | // value was a b-wire. 460 | self.fix_variable_assignment(ephemeral); 461 | 462 | // Now we emplace the variable into the linear combination. 463 | self.emplace_variable(&mut slots_available, &y, ephemeral, Coeff::One, q); 464 | num_slots_available -= 1; 465 | 466 | match (&mut current_value, &value) { 467 | (Some(ref mut current_value), Some(ref value)) => { 468 | current_value.add_assign(&value); 469 | }, 470 | _ => { 471 | current_value = None; 472 | } 473 | } 474 | 475 | assert!(num_slots_available == 0); 476 | 477 | // We're done, so return. 478 | return current_value; 479 | } else { 480 | self.emplace_variable(&mut slots_available, &y, term.0, term.1, q); 481 | num_slots_available -= 1; 482 | 483 | match (&mut current_value, self.backend.get_var(term.0)) { 484 | (Some(ref mut current_value), Some(mut value)) => { 485 | term.1.multiply(&mut value); 486 | current_value.add_assign(&value); 487 | }, 488 | _ => { 489 | current_value = None; 490 | } 491 | } 492 | } 493 | } else { 494 | // We're done, so return. 495 | return current_value; 496 | } 497 | } 498 | } 499 | 500 | // This takes a variable and coefficient and places it into a linear combination, 501 | // given a set of slots that are available, and updates the slot availability to 502 | // reflect which slot was chosen. 503 | fn emplace_variable(&mut self, slots_available: &mut [bool; M], y: &B::LinearConstraintIndex, var: Variable, coeff: Coeff, q: usize) 504 | { 505 | // Get the slots for this wire. 506 | let wire_slots = self.get_wire_slots(var); 507 | 508 | // Let's handle the simple case where the linear combination and the 509 | // variable have a slot that coincides. 510 | let mut available_i = None; 511 | for i in 0..M { 512 | if slots_available[i] { 513 | available_i = Some(i); 514 | 515 | if wire_slots[i] { 516 | self.emplace_slot(var, i, coeff, y, q); 517 | slots_available[i] = false; 518 | return; 519 | } 520 | } 521 | } 522 | 523 | let available_i = available_i.expect("there is always at least one slot open"); 524 | 525 | // available_i corresponds to a slot that is available in the linear 526 | // combination; clearly, it is not available for the wire. In order 527 | // to rectify this, we will create a new wire with the same value. 528 | let ephemeral_value = self.backend.get_var(var); 529 | let ephemeral = self.alloc(|| { 530 | ephemeral_value.ok_or(SynthesisError::AssignmentMissing) 531 | }).expect("assignment is provided so this should not fail"); 532 | 533 | // Now, we'll emplace the slot for _this_ variable. 534 | self.emplace_slot(ephemeral, available_i, coeff, y, q); 535 | slots_available[available_i] = false; 536 | 537 | // Next, we'll free up a slot in the original wire 538 | let free_i = (available_i + 1) % M; 539 | 540 | // by moving the term to the ephemeral wire. 541 | self.move_slot(free_i, var, ephemeral); 542 | 543 | // The original wire has slot free_i available now, and 544 | // the new wire has only available_i and (available_i + 1) % M 545 | // occupied. As long as M>=3, this means available_i + 2 % M 546 | // is a free wire for the ephemeral and it is distinct from 547 | // free_i! So, we can relate the ephemeral variable to the 548 | // original. 549 | let iter = [(var, Coeff::One), (ephemeral, Coeff::NegativeOne)]; 550 | let mut iter = iter.into_iter(); 551 | let iter: &mut Iterator)> = &mut iter; 552 | self.enforce_equals(iter.peekable(), None); 553 | } 554 | 555 | // Move slot value from wire to another 556 | fn move_slot(&mut self, slot: usize, from: Variable, to: Variable) { 557 | let slot_val; 558 | { 559 | let from_vals = match from { 560 | Variable::A(index) => &mut self.a[index - 1], 561 | Variable::B(index) => &mut self.b[index - 1], 562 | Variable::C(index) => &mut self.c[index - 1], 563 | }; 564 | 565 | if from_vals[slot].is_none() { 566 | // In this case, we do nothing. 567 | return; 568 | } 569 | 570 | slot_val = from_vals[slot].unwrap(); 571 | from_vals[slot] = None; 572 | } 573 | 574 | // We need the backend to compute the cached y^q value for us, 575 | // if it needs it. 576 | let y = self.backend.get_for_q(slot_val.1); 577 | 578 | self.backend.insert_coefficient(from, -slot_val.0, &y); // Negate coefficient to undo 579 | 580 | { 581 | let to_vals = match to { 582 | Variable::A(index) => &mut self.a[index - 1], 583 | Variable::B(index) => &mut self.b[index - 1], 584 | Variable::C(index) => &mut self.c[index - 1], 585 | }; 586 | 587 | to_vals[slot] = Some(slot_val); 588 | self.backend.insert_coefficient(to, slot_val.0, &y); 589 | } 590 | } 591 | 592 | // Place a coefficient in a slot 593 | fn emplace_slot(&mut self, var: Variable, slot_index: usize, coeff: Coeff, y: &B::LinearConstraintIndex, q: usize) 594 | { 595 | let vals = match var { 596 | Variable::A(index) => &mut self.a[index - 1], 597 | Variable::B(index) => &mut self.b[index - 1], 598 | Variable::C(index) => &mut self.c[index - 1], 599 | }; 600 | 601 | vals[slot_index] = Some((coeff, q)); 602 | 603 | self.backend.insert_coefficient(var, coeff, &y); 604 | } 605 | 606 | // Get available slots for a wire 607 | fn get_wire_slots(&self, var: Variable) -> [bool; M] { 608 | let vals = match var { 609 | Variable::A(index) => &self.a[index - 1], 610 | Variable::B(index) => &self.b[index - 1], 611 | Variable::C(index) => &self.c[index - 1], 612 | }; 613 | 614 | let mut slots = [true; M]; 615 | for i in 0..M { 616 | if vals[i].is_some() { 617 | slots[i] = false; 618 | } 619 | } 620 | 621 | slots 622 | } 623 | 624 | // If a variable changes value, we probably need to adjust. 625 | fn fix_variable_assignment(&mut self, var: Variable) { 626 | let index = var.get_index(); 627 | 628 | let a_value = self.backend.get_var(Variable::A(index)); 629 | let b_value = self.backend.get_var(Variable::B(index)); 630 | 631 | let c_value = match (a_value, b_value) { 632 | (Some(mut a), Some(b)) => { 633 | a.mul_assign(&b); 634 | Some(a) 635 | }, 636 | _ => { None } 637 | }; 638 | 639 | self.backend.set_var(Variable::C(index), || { 640 | c_value.ok_or(SynthesisError::AssignmentMissing) 641 | }).expect("assignment exists if the closure is called"); 642 | } 643 | } 644 | 645 | let mut tmp: Synthesizer = Synthesizer { 646 | backend: backend, 647 | current_variable: None, 648 | _marker: PhantomData, 649 | q: 0, 650 | n: 0, 651 | 652 | a: vec![], 653 | b: vec![], 654 | c: vec![], 655 | }; 656 | 657 | let one = tmp.alloc_input(|| Ok(E::Fr::one())).expect("should have no issues"); 658 | 659 | match (one, as ConstraintSystem>::ONE) { 660 | (Variable::A(1), Variable::A(1)) => {}, 661 | _ => panic!("one variable is incorrect") 662 | } 663 | 664 | circuit.synthesize(&mut tmp)?; 665 | 666 | // TODO: add blinding factors so we actually get zero-knowledge 667 | 668 | // println!("n = {}", tmp.n); 669 | 670 | Ok(()) 671 | } 672 | } -------------------------------------------------------------------------------- /src/util.rs: -------------------------------------------------------------------------------- 1 | use crate::SynthesisError; 2 | use merlin::Transcript; 3 | use pairing::{CurveAffine, CurveProjective, Engine, Field, PrimeField, PrimeFieldRepr}; 4 | use std::io; 5 | 6 | pub trait TranscriptProtocol { 7 | fn commit_point(&mut self, point: &G); 8 | fn commit_scalar(&mut self, scalar: &F); 9 | fn get_challenge_scalar(&mut self) -> F; 10 | } 11 | 12 | impl TranscriptProtocol for Transcript { 13 | fn commit_point(&mut self, point: &G) { 14 | self.commit_bytes(b"point", point.into_compressed().as_ref()); 15 | } 16 | 17 | fn commit_scalar(&mut self, scalar: &F) { 18 | let mut v = vec![]; 19 | scalar.into_repr().write_le(&mut v).unwrap(); 20 | 21 | self.commit_bytes(b"scalar", &v); 22 | } 23 | 24 | fn get_challenge_scalar(&mut self) -> F { 25 | loop { 26 | let mut repr: F::Repr = Default::default(); 27 | repr.read_be(TranscriptReader(self)).unwrap(); 28 | 29 | if let Ok(result) = F::from_repr(repr) { 30 | return result; 31 | } 32 | } 33 | } 34 | } 35 | 36 | struct TranscriptReader<'a>(&'a mut Transcript); 37 | 38 | impl<'a> io::Read for TranscriptReader<'a> { 39 | fn read(&mut self, buf: &mut [u8]) -> io::Result { 40 | self.0.challenge_bytes(b"read", buf); 41 | 42 | Ok(buf.len()) 43 | } 44 | } 45 | 46 | pub trait ChainExt: Iterator { 47 | fn chain_ext(self, other: U) -> Chain 48 | where 49 | Self: Sized, 50 | U: IntoIterator, 51 | { 52 | Chain { 53 | t: self, 54 | u: other.into_iter(), 55 | } 56 | } 57 | } 58 | 59 | impl ChainExt for I {} 60 | 61 | #[derive(Clone)] 62 | pub struct Chain { 63 | t: T, 64 | u: U, 65 | } 66 | 67 | impl Iterator for Chain 68 | where 69 | T: Iterator, 70 | U: Iterator, 71 | { 72 | type Item = T::Item; 73 | 74 | fn next(&mut self) -> Option { 75 | match self.t.next() { 76 | Some(v) => Some(v), 77 | None => match self.u.next() { 78 | Some(v) => Some(v), 79 | None => None, 80 | }, 81 | } 82 | } 83 | } 84 | 85 | impl ExactSizeIterator for Chain 86 | where 87 | T: Iterator, 88 | U: Iterator, 89 | T: ExactSizeIterator, 90 | U: ExactSizeIterator, 91 | { 92 | fn len(&self) -> usize { 93 | self.t.len() + self.u.len() 94 | } 95 | } 96 | 97 | impl DoubleEndedIterator for Chain 98 | where 99 | T: Iterator, 100 | U: Iterator, 101 | T: DoubleEndedIterator, 102 | U: DoubleEndedIterator, 103 | { 104 | fn next_back(&mut self) -> Option { 105 | match self.u.next_back() { 106 | Some(v) => Some(v), 107 | None => match self.t.next_back() { 108 | Some(v) => Some(v), 109 | None => None, 110 | }, 111 | } 112 | } 113 | } 114 | 115 | pub fn multiexp< 116 | 'a, 117 | G: CurveAffine, 118 | IB: IntoIterator, 119 | IS: IntoIterator, 120 | >( 121 | g: IB, 122 | s: IS, 123 | ) -> G::Projective 124 | where 125 | IB::IntoIter: ExactSizeIterator + Clone, 126 | IS::IntoIter: ExactSizeIterator, 127 | { 128 | let g = g.into_iter(); 129 | let s = s.into_iter(); 130 | assert_eq!(g.len(), s.len()); 131 | 132 | let c = if s.len() < 32 { 133 | 3u32 134 | } else { 135 | (f64::from(s.len() as u32)).ln().ceil() as u32 136 | }; 137 | 138 | // Convert all of the scalars into representations 139 | let mut s = s.map(|s| s.into_repr()).collect::>(); 140 | 141 | let mut windows = vec![]; 142 | let mut buckets = vec![]; 143 | 144 | let mask = (1u64 << c) - 1u64; 145 | let mut cur = 0; 146 | while cur <= ::Fr::NUM_BITS { 147 | let mut acc = G::Projective::zero(); 148 | 149 | buckets.truncate(0); 150 | buckets.resize((1 << c) - 1, G::Projective::zero()); 151 | 152 | let g = g.clone(); 153 | 154 | for (s, g) in s.iter_mut().zip(g) { 155 | let index = (s.as_ref()[0] & mask) as usize; 156 | 157 | if index != 0 { 158 | buckets[index - 1].add_assign_mixed(g); 159 | } 160 | 161 | s.shr(c as u32); 162 | } 163 | 164 | let mut running_sum = G::Projective::zero(); 165 | for exp in buckets.iter().rev() { 166 | running_sum.add_assign(exp); 167 | acc.add_assign(&running_sum); 168 | } 169 | 170 | windows.push(acc); 171 | 172 | cur += c; 173 | } 174 | 175 | let mut acc = G::Projective::zero(); 176 | 177 | for window in windows.into_iter().rev() { 178 | for _ in 0..c { 179 | acc.double(); 180 | } 181 | 182 | acc.add_assign(&window); 183 | } 184 | 185 | acc 186 | } 187 | 188 | /// Divides polynomial `a` in `x` by `x - b` with 189 | /// no remainder. 190 | pub fn kate_divison<'a, F: Field, I: IntoIterator>(a: I, mut b: F) -> Vec 191 | where 192 | I::IntoIter: DoubleEndedIterator + ExactSizeIterator, 193 | { 194 | b.negate(); 195 | let a = a.into_iter(); 196 | 197 | let mut q = vec![F::zero(); a.len() - 1]; 198 | 199 | let mut tmp = F::zero(); 200 | for (q, r) in q.iter_mut().rev().zip(a.rev()) { 201 | let mut lead_coeff = *r; 202 | lead_coeff.sub_assign(&tmp); 203 | *q = lead_coeff; 204 | tmp = lead_coeff; 205 | tmp.mul_assign(&b); 206 | } 207 | 208 | q 209 | } 210 | 211 | #[test] 212 | fn laurent_division() { 213 | use pairing::bls12_381::{Fr}; 214 | use pairing::PrimeField; 215 | 216 | let mut poly = vec![ 217 | Fr::from_str("328947234").unwrap(), 218 | Fr::from_str("3545623451111").unwrap(), 219 | Fr::from_str("112").unwrap(), 220 | Fr::from_str("55555").unwrap(), 221 | Fr::from_str("1235685").unwrap(), 222 | ]; 223 | 224 | fn eval(poly: &[Fr], point: Fr) -> Fr { 225 | let point_inv = point.inverse().unwrap(); 226 | 227 | let mut acc = Fr::zero(); 228 | let mut tmp = Fr::one(); 229 | for p in &poly[2..] { 230 | let mut t = *p; 231 | t.mul_assign(&tmp); 232 | acc.add_assign(&t); 233 | tmp.mul_assign(&point); 234 | } 235 | let mut tmp = point_inv; 236 | for p in poly[0..2].iter().rev() { 237 | let mut t = *p; 238 | t.mul_assign(&tmp); 239 | acc.add_assign(&t); 240 | tmp.mul_assign(&point_inv); 241 | } 242 | 243 | acc 244 | } 245 | 246 | let x = Fr::from_str("23").unwrap(); 247 | let z = Fr::from_str("2000").unwrap(); 248 | 249 | let p_at_x = eval(&poly, x); 250 | let p_at_z = eval(&poly, z); 251 | 252 | // poly = poly(X) - poly(z) 253 | poly[2].sub_assign(&p_at_z); 254 | 255 | let quotient_poly = kate_divison(&poly, z); 256 | 257 | let quotient = eval("ient_poly, x); 258 | 259 | // check that 260 | // quotient * (x - z) = p_at_x - p_at_z 261 | 262 | let mut lhs = x; 263 | lhs.sub_assign(&z); 264 | lhs.mul_assign("ient); 265 | 266 | let mut rhs = p_at_x; 267 | rhs.sub_assign(&p_at_z); 268 | 269 | assert_eq!(lhs, rhs); 270 | } 271 | 272 | pub fn multiply_polynomials(mut a: Vec, mut b: Vec) -> Vec { 273 | let result_len = a.len() + b.len() - 1; 274 | 275 | // Compute the size of our evaluation domain 276 | let mut m = 1; 277 | let mut exp = 0; 278 | while m < result_len { 279 | m *= 2; 280 | exp += 1; 281 | 282 | // The pairing-friendly curve may not be able to support 283 | // large enough (radix2) evaluation domains. 284 | if exp >= E::Fr::S { 285 | panic!("polynomial too large") 286 | } 287 | } 288 | 289 | // Compute omega, the 2^exp primitive root of unity 290 | let mut omega = E::Fr::root_of_unity(); 291 | for _ in exp..E::Fr::S { 292 | omega.square(); 293 | } 294 | 295 | // Extend with zeroes 296 | a.resize(m, E::Fr::zero()); 297 | b.resize(m, E::Fr::zero()); 298 | 299 | serial_fft::(&mut a[..], &omega, exp); 300 | serial_fft::(&mut b[..], &omega, exp); 301 | 302 | for (a, b) in a.iter_mut().zip(b.iter()) { 303 | a.mul_assign(b); 304 | } 305 | 306 | serial_fft::(&mut a[..], &omega.inverse().unwrap(), exp); 307 | 308 | a.truncate(result_len); 309 | 310 | let minv = E::Fr::from_str(&format!("{}", m)) 311 | .unwrap() 312 | .inverse() 313 | .unwrap(); 314 | 315 | for a in a.iter_mut() { 316 | a.mul_assign(&minv); 317 | } 318 | 319 | a 320 | } 321 | 322 | fn serial_fft(a: &mut [E::Fr], omega: &E::Fr, log_n: u32) { 323 | fn bitreverse(mut n: u32, l: u32) -> u32 { 324 | let mut r = 0; 325 | for _ in 0..l { 326 | r = (r << 1) | (n & 1); 327 | n >>= 1; 328 | } 329 | r 330 | } 331 | 332 | let n = a.len() as u32; 333 | assert_eq!(n, 1 << log_n); 334 | 335 | for k in 0..n { 336 | let rk = bitreverse(k, log_n); 337 | if k < rk { 338 | a.swap(rk as usize, k as usize); 339 | } 340 | } 341 | 342 | let mut m = 1; 343 | for _ in 0..log_n { 344 | let w_m = omega.pow(&[(n / (2 * m)) as u64]); 345 | 346 | let mut k = 0; 347 | while k < n { 348 | let mut w = E::Fr::one(); 349 | for j in 0..m { 350 | let mut t = a[(k + j + m) as usize]; 351 | t.mul_assign(&w); 352 | let mut tmp = a[(k + j) as usize]; 353 | tmp.sub_assign(&t); 354 | a[(k + j + m) as usize] = tmp; 355 | a[(k + j) as usize].add_assign(&t); 356 | w.mul_assign(&w_m); 357 | } 358 | 359 | k += 2 * m; 360 | } 361 | 362 | m *= 2; 363 | } 364 | } 365 | 366 | pub trait OptionExt { 367 | fn get(self) -> Result; 368 | } 369 | 370 | impl OptionExt for Option { 371 | fn get(self) -> Result { 372 | match self { 373 | Some(t) => Ok(t), 374 | None => Err(SynthesisError::AssignmentMissing), 375 | } 376 | } 377 | } 378 | --------------------------------------------------------------------------------