From aa50387d36c5a2795debe578c6ac1ee43f705b4e Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Fri, 9 Apr 2021 18:24:19 +0200 Subject: [PATCH 1/6] Started implementing FRI --- src/fri.rs | 72 +++++++++++++++++++++++++++++++ src/gadgets/merkle_proofs.rs | 83 +++++++++++++++++++++++++++++++++++- src/proof.rs | 5 ++- 3 files changed, 156 insertions(+), 4 deletions(-) diff --git a/src/fri.rs b/src/fri.rs index 3c166779..2275a362 100644 --- a/src/fri.rs +++ b/src/fri.rs @@ -1,3 +1,11 @@ +use crate::field::field::Field; +use crate::hash::{compress, hash_n_to_hash}; +use crate::plonk_challenger::Challenger; +use crate::polynomial::polynomial::{PolynomialCoeffs, PolynomialValues}; +use crate::proof::{Hash, FriProof}; +use crate::field::fft::fft; +use crate::gadgets::merkle_proofs::MerkleTree; + /// Somewhat arbitrary. Smaller values will increase delta, but with diminishing returns, /// while increasing L, potentially requiring more challenge points. const EPSILON: f64 = 0.01; @@ -10,6 +18,12 @@ struct FriConfig { /// a 4-to-1 reduction, then a 2-to-1 reduction. After these reductions, the reduced polynomial /// is sent directly. reduction_arity_bits: Vec, + + /// Number of reductions in the FRI protocol. So if the original domain has size `2^n`, + /// then the final domain will have size `2^(n-reduction_count)`. + reduction_count: usize, + + rate_bits: usize, } fn fri_delta(rate_log: usize, conjecture: bool) -> f64 { @@ -34,3 +48,61 @@ fn fri_l(codeword_len: usize, rate_log: usize, conjecture: bool) -> f64 { 1.0 / (2.0 * EPSILON * rate.sqrt()) } } + +// TODO: Different arity + PoW. +/// Performs a FRI round. +fn fri_round( + polynomial_coeffs: &PolynomialCoeffs, + polynomial_values: &PolynomialValues, + challenger: &mut Challenger, + config: &FriConfig, +) -> FriProof { + let n = polynomial_values.values.len(); + assert_eq!( + polynomial_coeffs.coeffs.len(), + n + ); + let mut trees = vec![MerkleTree::new(polynomial_values.values.iter().map(|&v| vec![v]).collect())]; + let mut root = trees.last().unwrap().root; + let mut coeffs = polynomial_coeffs.clone(); + let mut values; + + challenger.observe_hash(&root); + + // Commit phase + for _ in 0..config.reduction_count { + let beta = challenger.get_challenge(); + coeffs = PolynomialCoeffs::new( + coeffs + .coeffs + .chunks_exact(2) + .map(|chunk| chunk[0] + beta * chunk[1]) + .collect::>(), + ); + values = fft(coeffs.clone().lde(config.rate_bits)); + + let tree = MerkleTree::new(values.values.iter().map(|&v| vec![v]).collect()); + challenger.observe_hash(&tree.root); + trees.push(tree); + } + + // Query phase + let mut merkle_proofs = Vec::new(); + let mut evals = Vec::new(); + for i in 0..config.reduction_count { + let x = challenger.get_challenge(); + let x_index = (x.to_canonical_u64() as usize) % n; + let n2 = n>>1; + evals.extend(std::array::IntoIter::new([polynomial_values.values[x_index], polynomial_values.values[n2 + x_index]])); + merkle_proofs.extend(std::array::IntoIter::new([trees[i].prove(x_index), trees[i].prove(n2 + x_index)])); + } + + FriProof { + commit_phase_merkle_roots: trees.iter().map(|t| t.root).collect(), + initial_merkle_proofs: vec![], + intermediate_merkle_proofs: merkle_proofs, + final_poly: coeffs + } + +} + diff --git a/src/gadgets/merkle_proofs.rs b/src/gadgets/merkle_proofs.rs index 246406b6..595081ef 100644 --- a/src/gadgets/merkle_proofs.rs +++ b/src/gadgets/merkle_proofs.rs @@ -1,8 +1,10 @@ use crate::circuit_builder::CircuitBuilder; use crate::field::field::Field; +use crate::hash::{compress, hash_n_to_hash}; use crate::proof::{Hash, HashTarget}; use crate::target::Target; +#[derive(Clone, Debug)] pub struct MerkleProof { /// The Merkle digest of each sibling subtree, staying from the bottommost layer. pub siblings: Vec>, @@ -13,6 +15,55 @@ pub struct MerkleProofTarget { pub siblings: Vec, } +#[derive(Clone, Debug)] +pub struct MerkleTree { + /// The data in the leaves of the Merkle tree. + pub leaves: Vec>, + + /// The layers of hashes in the tree. The first layer is the one at the bottom. + pub layers: Vec>>, + + /// The Merkle root. + pub root: Hash, +} + +impl MerkleTree { + pub fn new(leaves: Vec>) -> Self { + let mut layers = vec![leaves.iter().map(|l| hash_n_to_hash(l.clone(), false)).collect::>()]; + loop { + match layers.last() { + Some(l) if l.len() > 1 => { + layers.push(l.chunks(2).map(|chunk| compress(chunk[0], chunk[1])).collect::>()); + }, + _ => break + } + } + let root = layers.pop().unwrap()[0]; + Self { + leaves, + layers, + root + } + + } + + /// Create a Merkle proof from a leaf index. + pub fn prove(&self, leaf_index: usize) -> MerkleProof { + MerkleProof { + siblings: self.layers + .iter() + .scan(leaf_index, |acc, layer| { + let index = *acc ^ 1; + *acc >>= 1; + Some(layer[index]) + }) + .collect(), + } + } +} + + + /// Verifies that the given leaf data is present at the given index in the Merkle tree with the /// given root. pub(crate) fn verify_merkle_proof( @@ -20,8 +71,18 @@ pub(crate) fn verify_merkle_proof( leaf_index: usize, merkle_root: Hash, proof: MerkleProof, -) { - todo!() +) -> bool { + let mut index = leaf_index; + let mut h = hash_n_to_hash(leaf_data, false); + for s in &proof.siblings { + h = if index & 1 == 0 { + compress(h, *s) + } else { + compress(*s, h) + }; + index >>= 1; + } + h == merkle_root } impl CircuitBuilder { @@ -37,3 +98,21 @@ impl CircuitBuilder { todo!() } } + +#[cfg(test)] +mod tests { + use super::*; + use crate::field::crandall_field::CrandallField; + + #[test] + fn test_merkle_proofs() { + type F = CrandallField; + let num_leaves = 128; + let leaves = (0..num_leaves).map(|_| vec![F::rand()]).collect::>(); + let tree = MerkleTree::new(leaves); + for i in 0..num_leaves { + let proof = tree.prove(i); + assert!(verify_merkle_proof(tree.leaves[i].clone(),i, tree.root, proof)); + } + } +} diff --git a/src/proof.rs b/src/proof.rs index d25196cb..f30ef028 100644 --- a/src/proof.rs +++ b/src/proof.rs @@ -1,9 +1,10 @@ use crate::field::field::Field; use crate::target::Target; use crate::gadgets::merkle_proofs::{MerkleProofTarget, MerkleProof}; +use crate::polynomial::polynomial::PolynomialCoeffs; /// Represents a ~256 bit hash output. -#[derive(Copy, Clone, Debug)] +#[derive(Copy, Clone, Debug, Eq, PartialEq)] pub struct Hash { pub(crate) elements: [F; 4], } @@ -60,7 +61,7 @@ pub struct FriProof { /// Merkle proofs for the reduced polynomials that were sent in the commit phase. pub intermediate_merkle_proofs: Vec>, /// The final polynomial in coefficient form. - pub final_poly: Vec, + pub final_poly: PolynomialCoeffs, } /// Represents a single FRI query, i.e. a path through the reduction tree. From 7ff41506798fe9917b464e0a282577bb882d1507 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Mon, 12 Apr 2021 10:38:07 +0200 Subject: [PATCH 2/6] Merge with merkle_proofs_2 --- Cargo.toml | 2 +- LICENSE-APACHE | 202 +++++++++++++++++++++++++++++++++++++ LICENSE-MIT | 22 ++++ README.md | 22 ++++ src/bin/bench_recursion.rs | 2 +- src/gadgets/hash.rs | 28 ++--- src/gadgets/mod.rs | 5 +- src/gadgets/split_join.rs | 31 +++--- src/gates/gmimc.rs | 102 +++++++++++++------ src/hash.rs | 71 +++++++++++-- src/lib.rs | 1 + src/merkle_proofs.rs | 100 ++++++++++++++++++ src/plonk_challenger.rs | 14 ++- src/plonk_common.rs | 2 + 14 files changed, 532 insertions(+), 72 deletions(-) create mode 100644 LICENSE-APACHE create mode 100644 LICENSE-MIT create mode 100644 src/merkle_proofs.rs diff --git a/Cargo.toml b/Cargo.toml index 0f2269ca..f82cc1de 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,7 +6,7 @@ authors = ["Daniel Lubarov "] readme = "README.md" license = "MIT OR Apache-2.0" repository = "https://github.com/mir-protocol/plonky2" -keywords = ["cryptography", "SNARK"] +keywords = ["cryptography", "SNARK", "FRI"] categories = ["cryptography"] edition = "2018" default-run = "bench_recursion" diff --git a/LICENSE-APACHE b/LICENSE-APACHE new file mode 100644 index 00000000..57bc88a1 --- /dev/null +++ b/LICENSE-APACHE @@ -0,0 +1,202 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + diff --git a/LICENSE-MIT b/LICENSE-MIT new file mode 100644 index 00000000..6220a485 --- /dev/null +++ b/LICENSE-MIT @@ -0,0 +1,22 @@ +MIT License + +Copyright (c) 2021 Predicate Labs Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/README.md b/README.md index f33850e4..e844ca77 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,25 @@ # plonky2 TODO: Write a readme... + + +## Disclaimer + +This code has not been thoroughly reviewed or tested, and should not be used in any production systems. + + +## License + +Licensed under either of + + * Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) + * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) + +at your option. + + +## Contribution + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in the work by you, as defined in the Apache-2.0 license, shall be +dual licensed as above, without any additional terms or conditions. diff --git a/src/bin/bench_recursion.rs b/src/bin/bench_recursion.rs index 9047d15a..81409642 100644 --- a/src/bin/bench_recursion.rs +++ b/src/bin/bench_recursion.rs @@ -39,7 +39,7 @@ fn bench_prove() { let gmimc_gate = GMiMCGate::::with_automatic_constants(); let config = CircuitConfig { - num_wires: 120, + num_wires: 134, num_routed_wires: 12, security_bits: 128, rate_bits: 3, diff --git a/src/gadgets/hash.rs b/src/gadgets/hash.rs index 8d0357a6..20b26244 100644 --- a/src/gadgets/hash.rs +++ b/src/gadgets/hash.rs @@ -3,37 +3,39 @@ use std::convert::TryInto; use crate::circuit_builder::CircuitBuilder; use crate::field::field::Field; use crate::gates::gmimc::GMiMCGate; -use crate::gates::noop::NoopGate; use crate::hash::GMIMC_ROUNDS; use crate::target::Target; use crate::wire::Wire; +// TODO: Move to be next to native `permute`? impl CircuitBuilder { pub fn permute(&mut self, inputs: [Target; 12]) -> [Target; 12] { let zero = self.zero(); - self.permute_switched(inputs, zero) - } - - pub(crate) fn permute_switched(&mut self, inputs: [Target; 12], switch: Target) -> [Target; 12] { let gate = self.add_gate_no_constants( GMiMCGate::::with_automatic_constants()); - let switch_wire = GMiMCGate::::WIRE_SWITCH; - let switch_wire = Target::Wire(Wire { gate, input: switch_wire }); - self.route(switch, switch_wire); + // We don't want to swap any inputs, so set that wire to 0. + let swap_wire = GMiMCGate::::WIRE_SWAP; + let swap_wire = Target::Wire(Wire { gate, input: swap_wire }); + self.route(zero, swap_wire); + // The old accumulator wire doesn't matter, since we won't read the new accumulator wire. + // We do have to set it to something though, so we'll arbitrary pick 0. + let old_acc_wire = GMiMCGate::::WIRE_INDEX_ACCUMULATOR_OLD; + let old_acc_wire = Target::Wire(Wire { gate, input: old_acc_wire }); + self.route(zero, old_acc_wire); + + // Route input wires. for i in 0..12 { - let in_wire = GMiMCGate::::wire_output(i); + let in_wire = GMiMCGate::::wire_input(i); let in_wire = Target::Wire(Wire { gate, input: in_wire }); self.route(inputs[i], in_wire); } - // Add a NoopGate just to receive the outputs. - let next_gate = self.add_gate_no_constants(NoopGate::get()); - + // Collect output wires. (0..12) .map(|i| Target::Wire( - Wire { gate: next_gate, input: GMiMCGate::::wire_output(i) })) + Wire { gate, input: GMiMCGate::::wire_output(i) })) .collect::>() .try_into() .unwrap() diff --git a/src/gadgets/mod.rs b/src/gadgets/mod.rs index 8407efe7..ed84207e 100644 --- a/src/gadgets/mod.rs +++ b/src/gadgets/mod.rs @@ -1,4 +1,3 @@ -pub(crate) mod arithmetic; -pub(crate) mod hash; -pub(crate) mod merkle_proofs; +pub mod arithmetic; +pub mod hash; pub(crate) mod split_join; diff --git a/src/gadgets/split_join.rs b/src/gadgets/split_join.rs index a6458440..21821706 100644 --- a/src/gadgets/split_join.rs +++ b/src/gadgets/split_join.rs @@ -3,20 +3,25 @@ use crate::generator::{SimpleGenerator, WitnessGenerator}; use crate::target::Target; use crate::wire::Wire; use crate::witness::PartialWitness; +use crate::circuit_builder::CircuitBuilder; -// /// Constraints for a little-endian split. -// pub fn split_le_constraints( -// integer: ConstraintPolynomial, -// bits: &[ConstraintPolynomial], -// ) -> Vec> { -// let weighted_sum = bits.iter() -// .fold(ConstraintPolynomial::zero(), |acc, b| acc.double() + b); -// bits.iter() -// .rev() -// .map(|b| b * (b - 1)) -// .chain(iter::once(weighted_sum - integer)) -// .collect() -// } +impl CircuitBuilder { + /// Split the given integer into a list of virtual advice targets, where each one represents a + /// bit of the integer, with little-endian ordering. + /// + /// Note that this only handles witness generation; it does not enforce that the decomposition + /// is correct. The output should be treated as a "purported" decomposition which must be + /// enforced elsewhere. + pub(crate) fn split_le_virtual( + &mut self, + integer: Target, + num_bits: usize, + ) -> Vec { + let bit_targets = self.add_virtual_advice_targets(num_bits); + split_le_generator::(integer, bit_targets.clone()); + bit_targets + } +} /// Generator for a little-endian split. pub fn split_le_generator( diff --git a/src/gates/gmimc.rs b/src/gates/gmimc.rs index dce5f9bb..dad8b078 100644 --- a/src/gates/gmimc.rs +++ b/src/gates/gmimc.rs @@ -1,12 +1,12 @@ use std::sync::Arc; use crate::circuit_builder::CircuitBuilder; -use crate::vars::{EvaluationTargets, EvaluationVars}; use crate::field::field::Field; use crate::gates::gate::{Gate, GateRef}; use crate::generator::{SimpleGenerator, WitnessGenerator}; use crate::gmimc::gmimc_automatic_constants; use crate::target::Target; +use crate::vars::{EvaluationTargets, EvaluationVars}; use crate::wire::Wire; use crate::witness::PartialWitness; @@ -15,6 +15,11 @@ const W: usize = 12; /// Evaluates a full GMiMC permutation with 12 state elements, and writes the output to the next /// gate's first `width` wires (which could be the input of another `GMiMCGate`). +/// +/// This also has some extra features to make it suitable for efficiently verifying Merkle proofs. +/// It has a flag which can be used to swap the first four inputs with the next four, for ordering +/// sibling digests. It also has an accumulator that computes the weighted sum of these flags, for +/// computing the index of the leaf based on these swap bits. #[derive(Debug)] pub struct GMiMCGate { constants: Arc<[F; R]>, @@ -31,24 +36,27 @@ impl GMiMCGate { Self::with_constants(constants) } - /// If this is set to 1, the first four inputs will be swapped with the next four inputs. This - /// is useful for ordering hashes in Merkle proofs. Otherwise, this should be set to 0. - pub const WIRE_SWITCH: usize = W; - - /// The wire index for the i'th input to the permutation. + /// The wire index for the `i`th input to the permutation. pub fn wire_input(i: usize) -> usize { i } - /// The wire index for the i'th output to the permutation. - /// Note that outputs are written to the next gate's wires. + /// The wire index for the `i`th output to the permutation. pub fn wire_output(i: usize) -> usize { - i + W + i } + /// Used to incrementally compute the index of the leaf based on a series of swap bits. + pub const WIRE_INDEX_ACCUMULATOR_OLD: usize = 2 * W; + pub const WIRE_INDEX_ACCUMULATOR_NEW: usize = 2 * W + 1; + + /// If this is set to 1, the first four inputs will be swapped with the next four inputs. This + /// is useful for ordering hashes in Merkle proofs. Otherwise, this should be set to 0. + pub const WIRE_SWAP: usize = 2 * W + 2; + /// A wire which stores the input to the `i`th cubing. fn wire_cubing_input(i: usize) -> usize { - W + 1 + i + 2 * W + 3 + i } } @@ -61,26 +69,34 @@ impl Gate for GMiMCGate { fn eval_unfiltered(&self, vars: EvaluationVars) -> Vec { let mut constraints = Vec::with_capacity(W + R); - // Value that is implicitly added to each element. - // See https://affine.group/2020/02/starkware-challenge - let mut addition_buffer = F::ZERO; + // Assert that `swap` is binary. + let swap = vars.local_wires[Self::WIRE_SWAP]; + constraints.push(swap * (swap - F::ONE)); + + let old_index_acc = vars.local_wires[Self::WIRE_INDEX_ACCUMULATOR_OLD]; + let new_index_acc = vars.local_wires[Self::WIRE_INDEX_ACCUMULATOR_NEW]; + let computed_new_index_acc = F::TWO * old_index_acc + swap; + constraints.push(computed_new_index_acc - new_index_acc); - let switch = vars.local_wires[Self::WIRE_SWITCH]; let mut state = Vec::with_capacity(12); for i in 0..4 { let a = vars.local_wires[i]; let b = vars.local_wires[i + 4]; - state.push(a + switch * (b - a)); + state.push(a + swap * (b - a)); } for i in 0..4 { let a = vars.local_wires[i + 4]; let b = vars.local_wires[i]; - state.push(a + switch * (b - a)); + state.push(a + swap * (b - a)); } for i in 8..12 { state.push(vars.local_wires[i]); } + // Value that is implicitly added to each element. + // See https://affine.group/2020/02/starkware-challenge + let mut addition_buffer = F::ZERO; + for r in 0..R { let active = r % W; let cubing_input = state[active] + addition_buffer + self.constants[r]; @@ -93,7 +109,7 @@ impl Gate for GMiMCGate { for i in 0..W { state[i] += addition_buffer; - constraints.push(state[i] - vars.next_wires[i]); + constraints.push(state[i] - vars.local_wires[Self::wire_output(i)]); } constraints @@ -133,7 +149,7 @@ impl Gate for GMiMCGate { } fn num_constraints(&self) -> usize { - R + W + R + W + 2 } } @@ -145,11 +161,15 @@ struct GMiMCGenerator { impl SimpleGenerator for GMiMCGenerator { fn dependencies(&self) -> Vec { - (0..W) - .map(|i| Target::Wire(Wire { - gate: self.gate_index, - input: GMiMCGate::::wire_input(i), - })) + let mut dep_input_indices = Vec::with_capacity(W + 2); + for i in 0..W { + dep_input_indices.push(GMiMCGate::::wire_input(i)); + } + dep_input_indices.push(GMiMCGate::::WIRE_SWAP); + dep_input_indices.push(GMiMCGate::::WIRE_INDEX_ACCUMULATOR_OLD); + + dep_input_indices.into_iter() + .map(|input| Target::Wire(Wire { gate: self.gate_index, input })) .collect() } @@ -163,17 +183,30 @@ impl SimpleGenerator for GMiMCGenerator { })) .collect::>(); - let switch_value = witness.get_wire(Wire { + let swap_value = witness.get_wire(Wire { gate: self.gate_index, - input: GMiMCGate::::WIRE_SWITCH, + input: GMiMCGate::::WIRE_SWAP, }); - debug_assert!(switch_value == F::ZERO || switch_value == F::ONE); - if switch_value == F::ONE { + debug_assert!(swap_value == F::ZERO || swap_value == F::ONE); + if swap_value == F::ONE { for i in 0..4 { state.swap(i, 4 + i); } } + // Update the index accumulator. + let old_index_acc_value = witness.get_wire(Wire { + gate: self.gate_index, + input: GMiMCGate::::WIRE_INDEX_ACCUMULATOR_OLD, + }); + let new_index_acc_value = F::TWO * old_index_acc_value + swap_value; + result.set_wire( + Wire { + gate: self.gate_index, + input: GMiMCGate::::WIRE_INDEX_ACCUMULATOR_NEW, + }, + new_index_acc_value); + // Value that is implicitly added to each element. // See https://affine.group/2020/02/starkware-challenge let mut addition_buffer = F::ZERO; @@ -196,7 +229,7 @@ impl SimpleGenerator for GMiMCGenerator { state[i] += addition_buffer; result.set_wire( Wire { - gate: self.gate_index + 1, + gate: self.gate_index, input: GMiMCGate::::wire_output(i), }, state[i]); @@ -239,7 +272,12 @@ mod tests { .collect::>(); let mut witness = PartialWitness::new(); - witness.set_wire(Wire { gate: 0, input: Gate::WIRE_SWITCH }, F::ZERO); + witness.set_wire( + Wire { gate: 0, input: Gate::WIRE_INDEX_ACCUMULATOR_OLD }, + F::from_canonical_usize(7)); + witness.set_wire( + Wire { gate: 0, input: Gate::WIRE_SWAP }, + F::ZERO); for i in 0..W { witness.set_wire( Wire { gate: 0, input: Gate::wire_input(i) }, @@ -255,8 +293,12 @@ mod tests { for i in 0..W { let out = witness.get_wire( - Wire { gate: 1, input: Gate::wire_output(i) }); + Wire { gate: 0, input: Gate::wire_output(i) }); assert_eq!(out, expected_outputs[i]); } + + let acc_new = witness.get_wire( + Wire { gate: 0, input: Gate::WIRE_INDEX_ACCUMULATOR_NEW }); + assert_eq!(acc_new, F::from_canonical_usize(7 * 2)); } } diff --git a/src/hash.rs b/src/hash.rs index 9ac7b120..e90cdb0a 100644 --- a/src/hash.rs +++ b/src/hash.rs @@ -6,8 +6,10 @@ use rayon::prelude::*; use crate::field::field::Field; use crate::gmimc::gmimc_permute_array; -use crate::proof::Hash; +use crate::proof::{Hash, HashTarget}; use crate::util::reverse_index_bits_in_place; +use crate::circuit_builder::CircuitBuilder; +use crate::target::Target; pub(crate) const SPONGE_RATE: usize = 8; pub(crate) const SPONGE_CAPACITY: usize = 4; @@ -25,7 +27,7 @@ const ELEMS_PER_CHUNK: usize = 1 << 8; /// Hash the vector if necessary to reduce its length to ~256 bits. If it already fits, this is a /// no-op. -pub fn hash_or_noop(mut inputs: Vec) -> Hash { +pub fn hash_or_noop(inputs: Vec) -> Hash { if inputs.len() <= 4 { Hash::from_partial(inputs) } else { @@ -33,6 +35,64 @@ pub fn hash_or_noop(mut inputs: Vec) -> Hash { } } +impl CircuitBuilder { + pub fn hash_or_noop(&mut self, inputs: Vec) -> HashTarget { + let zero = self.zero(); + if inputs.len() <= 4 { + HashTarget::from_partial(inputs, zero) + } else { + self.hash_n_to_hash(inputs, false) + } + } + + pub fn hash_n_to_hash(&mut self, inputs: Vec, pad: bool) -> HashTarget { + HashTarget::from_vec(self.hash_n_to_m(inputs, 4, pad)) + } + + pub fn hash_n_to_m( + &mut self, + mut inputs: Vec, + num_outputs: usize, + pad: bool, + ) -> Vec { + let zero = self.zero(); + let one = self.one(); + + if pad { + inputs.push(zero); + while (inputs.len() + 1) % SPONGE_WIDTH != 0 { + inputs.push(one); + } + inputs.push(zero); + } + + let mut state = [zero; SPONGE_WIDTH]; + + // Absorb all input chunks. + for input_chunk in inputs.chunks(SPONGE_RATE) { + // Overwrite the first r elements with the inputs. This differs from a standard sponge, + // where we would xor or add in the inputs. This is a well-known variant, though, + // sometimes called "overwrite mode". + for i in 0..input_chunk.len() { + state[i] = input_chunk[i]; + } + state = self.permute(state); + } + + // Squeeze until we have the desired number of outputs. + let mut outputs = Vec::new(); + loop { + for i in 0..SPONGE_RATE { + outputs.push(state[i]); + if outputs.len() == num_outputs { + return outputs; + } + } + state = self.permute(state); + } + } +} + /// A one-way compression function which takes two ~256 bit inputs and returns a ~256 bit output. pub fn compress(x: Hash, y: Hash) -> Hash { let mut inputs = Vec::with_capacity(8); @@ -60,7 +120,7 @@ pub fn hash_n_to_m(mut inputs: Vec, num_outputs: usize, pad: bool) let mut state = [F::ZERO; SPONGE_WIDTH]; // Absorb all input chunks. - for input_chunk in inputs.chunks(SPONGE_WIDTH - 1) { + for input_chunk in inputs.chunks(SPONGE_RATE) { for i in 0..input_chunk.len() { state[i] += input_chunk[i]; } @@ -70,7 +130,7 @@ pub fn hash_n_to_m(mut inputs: Vec, num_outputs: usize, pad: bool) // Squeeze until we have the desired number of outputs. let mut outputs = Vec::new(); loop { - for i in 0..(SPONGE_WIDTH - 1) { + for i in 0..SPONGE_RATE { outputs.push(state[i]); if outputs.len() == num_outputs { return outputs; @@ -81,8 +141,7 @@ pub fn hash_n_to_m(mut inputs: Vec, num_outputs: usize, pad: bool) } pub fn hash_n_to_hash(inputs: Vec, pad: bool) -> Hash { - let elements = hash_n_to_m(inputs, 4, pad).try_into().unwrap(); - Hash { elements } + Hash::from_vec(hash_n_to_m(inputs, 4, pad)) } pub fn hash_n_to_1(inputs: Vec, pad: bool) -> F { diff --git a/src/lib.rs b/src/lib.rs index 5f840e78..89762ead 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,6 +8,7 @@ pub mod gates; pub mod generator; pub mod gmimc; pub mod hash; +pub mod merkle_proofs; pub mod plonk_challenger; pub mod plonk_common; pub mod polynomial; diff --git a/src/merkle_proofs.rs b/src/merkle_proofs.rs new file mode 100644 index 00000000..5f735e35 --- /dev/null +++ b/src/merkle_proofs.rs @@ -0,0 +1,100 @@ +use crate::circuit_builder::CircuitBuilder; +use crate::field::field::Field; +use crate::gates::gmimc::GMiMCGate; +use crate::hash::{compress, hash_or_noop}; +use crate::hash::GMIMC_ROUNDS; +use crate::proof::{Hash, HashTarget}; +use crate::target::Target; +use crate::wire::Wire; + +pub struct MerkleProof { + /// The Merkle digest of each sibling subtree, staying from the bottommost layer. + pub siblings: Vec>, +} + +pub struct MerkleProofTarget { + /// The Merkle digest of each sibling subtree, staying from the bottommost layer. + pub siblings: Vec, +} + +/// Verifies that the given leaf data is present at the given index in the Merkle tree with the +/// given root. +pub(crate) fn verify_merkle_proof( + leaf_data: Vec, + leaf_index: usize, + merkle_root: Hash, + proof: MerkleProof, +) -> bool { + let mut current_digest = hash_or_noop(leaf_data); + for (i, sibling_digest) in proof.siblings.into_iter().enumerate() { + let bit = (leaf_index >> i & 1) == 1; + current_digest = if bit { + compress(sibling_digest, current_digest) + } else { + compress(current_digest, sibling_digest) + } + } + current_digest == merkle_root +} + +impl CircuitBuilder { + /// Verifies that the given leaf data is present at the given index in the Merkle tree with the + /// given root. + pub(crate) fn verify_merkle_proof( + &mut self, + leaf_data: Vec, + leaf_index: Target, + merkle_root: HashTarget, + proof: MerkleProofTarget, + ) { + let zero = self.zero(); + let height = proof.siblings.len(); + let purported_index_bits = self.split_le_virtual(leaf_index, height); + + let mut state: HashTarget = self.hash_or_noop(leaf_data); + let mut acc_leaf_index = zero; + + for (bit, sibling) in purported_index_bits.into_iter().zip(proof.siblings) { + let gate = self.add_gate_no_constants( + GMiMCGate::::with_automatic_constants()); + + let swap_wire = GMiMCGate::::WIRE_SWAP; + let swap_wire = Target::Wire(Wire { gate, input: swap_wire }); + self.generate_copy(bit, swap_wire); + + let old_acc_wire = GMiMCGate::::WIRE_INDEX_ACCUMULATOR_OLD; + let old_acc_wire = Target::Wire(Wire { gate, input: old_acc_wire }); + self.route(acc_leaf_index, old_acc_wire); + + let new_acc_wire = GMiMCGate::::WIRE_INDEX_ACCUMULATOR_NEW; + let new_acc_wire = Target::Wire(Wire { gate, input: new_acc_wire }); + acc_leaf_index = new_acc_wire; + + let input_wires = (0..12) + .map(|i| Target::Wire( + Wire { gate, input: GMiMCGate::::wire_input(i) })) + .collect::>(); + + for i in 0..4 { + self.route(state.elements[i], input_wires[i]); + self.route(sibling.elements[i], input_wires[4 + i]); + self.route(zero, input_wires[8 + i]); + } + + state = HashTarget::from_vec((0..4) + .map(|i| Target::Wire( + Wire { gate, input: GMiMCGate::::wire_output(i) })) + .collect()) + } + + self.assert_equal(acc_leaf_index, leaf_index); + + self.assert_hashes_equal(state, merkle_root) + } + + pub(crate) fn assert_hashes_equal(&mut self, x: HashTarget, y: HashTarget) { + for i in 0..4 { + self.assert_equal(x.elements[i], y.elements[i]); + } + } +} diff --git a/src/plonk_challenger.rs b/src/plonk_challenger.rs index 8dbd2813..b745150d 100644 --- a/src/plonk_challenger.rs +++ b/src/plonk_challenger.rs @@ -79,9 +79,11 @@ impl Challenger { /// Absorb any buffered inputs. After calling this, the input buffer will be empty. fn absorb_buffered_inputs(&mut self) { for input_chunk in self.input_buffer.chunks(SPONGE_RATE) { - // Add the inputs to our sponge state. + // Overwrite the first r elements with the inputs. This differs from a standard sponge, + // where we would xor or add in the inputs. This is a well-known variant, though, + // sometimes called "overwrite mode". for (i, &input) in input_chunk.iter().enumerate() { - self.sponge_state[i] = self.sponge_state[i] + input; + self.sponge_state[i] = input; } // Apply the permutation. @@ -177,9 +179,11 @@ impl RecursiveChallenger { builder: &mut CircuitBuilder, ) { for input_chunk in self.input_buffer.chunks(SPONGE_RATE) { - // Add the inputs to our sponge state. + // Overwrite the first r elements with the inputs. This differs from a standard sponge, + // where we would xor or add in the inputs. This is a well-known variant, though, + // sometimes called "overwrite mode". for (i, &input) in input_chunk.iter().enumerate() { - self.sponge_state[i] = builder.add(self.sponge_state[i], input); + self.sponge_state[i] = input; } // Apply the permutation. @@ -228,7 +232,7 @@ mod tests { let config = CircuitConfig { num_wires: 114, - num_routed_wires: 13, + num_routed_wires: 27, ..CircuitConfig::default() }; let mut builder = CircuitBuilder::::new(config); diff --git a/src/plonk_common.rs b/src/plonk_common.rs index c1965995..1c2deb02 100644 --- a/src/plonk_common.rs +++ b/src/plonk_common.rs @@ -18,6 +18,8 @@ pub fn evaluate_gate_constraints( for gate in gates { let gate_constraints = gate.0.eval_filtered(vars); for (i, c) in gate_constraints.into_iter().enumerate() { + debug_assert!(i < num_gate_constraints, + "num_constraints() gave too low of a number"); constraints[i] += c; } } From 6b407e45efd6ffd6d85e8b7a68bd8a3974b693ea Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Wed, 21 Apr 2021 22:31:45 +0200 Subject: [PATCH 3/6] Progress on FRI --- src/bin/bench_field_mul.rs | 5 +- src/bin/bench_gmimc.rs | 37 +-- src/circuit_builder.rs | 84 ++++-- src/circuit_data.rs | 3 +- src/field/cosets.rs | 17 +- src/field/crandall_field.rs | 8 +- src/field/fft.rs | 17 +- src/field/field.rs | 57 +++-- src/field/field_testing.rs | 112 ++++---- src/field/mod.rs | 6 +- src/fri.rs | 212 ++++++++++++++-- src/gadgets/arithmetic.rs | 22 +- src/gadgets/hash.rs | 27 +- src/gadgets/split_join.rs | 23 +- src/gates/arithmetic.rs | 5 +- src/gates/constant.rs | 7 +- src/gates/gate.rs | 2 +- src/gates/gmimc.rs | 76 ++++-- src/gates/gmimc_eval.rs | 2 +- src/gates/noop.rs | 2 +- src/gmimc.rs | 23 +- src/hash.rs | 117 ++++++++- src/lib.rs | 3 +- src/merkle_proofs.rs | 61 +++-- src/merkle_tree.rs | 118 +++++++++ src/plonk_challenger.rs | 21 +- src/plonk_common.rs | 11 +- src/polynomial/division.rs | 3 +- src/polynomial/polynomial.rs | 25 +- src/proof.rs | 41 ++- src/prover.rs | 141 ++++++---- src/rescue.rs | 480 ++++++++++++++++++++++++++++++++--- src/util.rs | 12 +- src/witness.rs | 13 +- 34 files changed, 1420 insertions(+), 373 deletions(-) create mode 100644 src/merkle_tree.rs diff --git a/src/bin/bench_field_mul.rs b/src/bin/bench_field_mul.rs index 4a1b7e04..66b36ffd 100644 --- a/src/bin/bench_field_mul.rs +++ b/src/bin/bench_field_mul.rs @@ -17,5 +17,8 @@ fn main() { println!("result {:?}", x); println!("took {:?}", duration); - println!("avg {:?}ns", duration.as_secs_f64() * 1e9 / (num_muls as f64)); + println!( + "avg {:?}ns", + duration.as_secs_f64() * 1e9 / (num_muls as f64) + ); } diff --git a/src/bin/bench_gmimc.rs b/src/bin/bench_gmimc.rs index 5f7140d7..f234285d 100644 --- a/src/bin/bench_gmimc.rs +++ b/src/bin/bench_gmimc.rs @@ -17,24 +17,29 @@ fn main() { const W: usize = 13; const HASHES_PER_POLY: usize = 1 << (13 + LDE_BITS); - let threads = (0..THREADS).map(|_i| { - thread::spawn(move || { - let mut x = [F::ZERO; W]; - for i in 0..W { - x[i] = F::from_canonical_u64((i as u64) * 123456 + 789); - } + let threads = (0..THREADS) + .map(|_i| { + thread::spawn(move || { + let mut x = [F::ZERO; W]; + for i in 0..W { + x[i] = F::from_canonical_u64((i as u64) * 123456 + 789); + } - let hashes_per_thread = HASHES_PER_POLY * PROVER_POLYS / THREADS; - let start = Instant::now(); - for _ in 0..hashes_per_thread { - x = gmimc_permute_array::<_, W, GMIMC_ROUNDS>(x, GMIMC_CONSTANTS); - } - let duration = start.elapsed(); - println!("took {:?}", duration); - println!("avg {:?}us", duration.as_secs_f64() * 1e6 / (hashes_per_thread as f64)); - println!("result {:?}", x); + let hashes_per_thread = HASHES_PER_POLY * PROVER_POLYS / THREADS; + let start = Instant::now(); + for _ in 0..hashes_per_thread { + x = gmimc_permute_array::<_, W, GMIMC_ROUNDS>(x, GMIMC_CONSTANTS); + } + let duration = start.elapsed(); + println!("took {:?}", duration); + println!( + "avg {:?}us", + duration.as_secs_f64() * 1e6 / (hashes_per_thread as f64) + ); + println!("result {:?}", x); + }) }) - }).collect::>(); + .collect::>(); for t in threads { t.join().expect("oops"); diff --git a/src/circuit_builder.rs b/src/circuit_builder.rs index 71a3d245..0539a551 100644 --- a/src/circuit_builder.rs +++ b/src/circuit_builder.rs @@ -3,14 +3,17 @@ use std::time::Instant; use log::info; -use crate::circuit_data::{CircuitConfig, CircuitData, CommonCircuitData, ProverCircuitData, ProverOnlyCircuitData, VerifierCircuitData, VerifierOnlyCircuitData}; +use crate::circuit_data::{ + CircuitConfig, CircuitData, CommonCircuitData, ProverCircuitData, ProverOnlyCircuitData, + VerifierCircuitData, VerifierOnlyCircuitData, +}; +use crate::field::cosets::get_unique_coset_shifts; use crate::field::field::Field; use crate::gates::constant::ConstantGate; use crate::gates::gate::{GateInstance, GateRef}; use crate::gates::noop::NoopGate; use crate::generator::{CopyGenerator, WitnessGenerator}; use crate::hash::merkle_root_bit_rev_order; -use crate::field::cosets::get_unique_coset_shifts; use crate::polynomial::polynomial::PolynomialValues; use crate::target::Target; use crate::util::{log2_strict, transpose, transpose_poly_values}; @@ -79,14 +82,21 @@ impl CircuitBuilder { // TODO: Not passing next constants for now. Not sure if it's really useful... self.add_generators(gate_type.0.generators(index, &constants, &[])); - self.gate_instances.push(GateInstance { gate_type, constants }); + self.gate_instances.push(GateInstance { + gate_type, + constants, + }); index } fn check_gate_compatibility(&self, gate: &GateRef) { - assert!(gate.0.num_wires() <= self.config.num_wires, - "{:?} requires {} wires, but our GateConfig has only {}", - gate.0.id(), gate.0.num_wires(), self.config.num_wires); + assert!( + gate.0.num_wires() <= self.config.num_wires, + "{:?} requires {} wires, but our GateConfig has only {}", + gate.0.id(), + gate.0.num_wires(), + self.config.num_wires + ); } /// Shorthand for `generate_copy` and `assert_equal`. @@ -104,8 +114,14 @@ impl CircuitBuilder { /// Uses Plonk's permutation argument to require that two elements be equal. /// Both elements must be routable, otherwise this method will panic. pub fn assert_equal(&mut self, x: Target, y: Target) { - assert!(x.is_routable(self.config), "Tried to route a wire that isn't routable"); - assert!(y.is_routable(self.config), "Tried to route a wire that isn't routable"); + assert!( + x.is_routable(self.config), + "Tried to route a wire that isn't routable" + ); + assert!( + y.is_routable(self.config), + "Tried to route a wire that isn't routable" + ); // TODO: Add to copy_constraints. } @@ -140,7 +156,10 @@ impl CircuitBuilder { /// Returns a routable target with the given constant value. pub fn constant(&mut self, c: F) -> Target { let gate = self.add_gate(ConstantGate::get(), vec![c]); - Target::Wire(Wire { gate, input: ConstantGate::WIRE_OUTPUT }) + Target::Wire(Wire { + gate, + input: ConstantGate::WIRE_OUTPUT, + }) } pub fn constants(&mut self, constants: &[F]) -> Vec { @@ -156,11 +175,15 @@ impl CircuitBuilder { } fn constant_polys(&self) -> Vec> { - let num_constants = self.gate_instances.iter() + let num_constants = self + .gate_instances + .iter() .map(|gate_inst| gate_inst.constants.len()) .max() .unwrap(); - let constants_per_gate = self.gate_instances.iter() + let constants_per_gate = self + .gate_instances + .iter() .map(|gate_inst| { let mut padded_constants = gate_inst.constants.clone(); for _ in padded_constants.len()..num_constants { @@ -177,13 +200,17 @@ impl CircuitBuilder { } fn sigma_vecs(&self) -> Vec> { - vec![PolynomialValues::zero(self.gate_instances.len()); self.config.num_routed_wires] // TODO + vec![PolynomialValues::zero(self.gate_instances.len()); self.config.num_routed_wires] + // TODO } /// Builds a "full circuit", with both prover and verifier data. pub fn build(mut self) -> CircuitData { let start = Instant::now(); - info!("degree before blinding & padding: {}", self.gate_instances.len()); + info!( + "degree before blinding & padding: {}", + self.gate_instances.len() + ); self.blind_and_pad(); let degree = self.gate_instances.len(); info!("degree after blinding & padding: {}", degree); @@ -199,7 +226,11 @@ impl CircuitBuilder { let sigmas_root = merkle_root_bit_rev_order(sigma_ldes_t.clone()); let generators = self.generators; - let prover_only = ProverOnlyCircuitData { generators, constant_ldes_t, sigma_ldes_t }; + let prover_only = ProverOnlyCircuitData { + generators, + constant_ldes_t, + sigma_ldes_t, + }; let verifier_only = VerifierOnlyCircuitData {}; // The HashSet of gates will have a non-deterministic order. When converting to a Vec, we @@ -207,7 +238,8 @@ impl CircuitBuilder { let mut gates = self.gates.iter().cloned().collect::>(); gates.sort_unstable_by_key(|gate| gate.0.id()); - let num_gate_constraints = gates.iter() + let num_gate_constraints = gates + .iter() .map(|gate| gate.0.num_constraints()) .max() .expect("No gates?"); @@ -236,14 +268,28 @@ impl CircuitBuilder { /// Builds a "prover circuit", with data needed to generate proofs but not verify them. pub fn build_prover(self) -> ProverCircuitData { // TODO: Can skip parts of this. - let CircuitData { prover_only, common, .. } = self.build(); - ProverCircuitData { prover_only, common } + let CircuitData { + prover_only, + common, + .. + } = self.build(); + ProverCircuitData { + prover_only, + common, + } } /// Builds a "verifier circuit", with data needed to verify proofs but not generate them. pub fn build_verifier(self) -> VerifierCircuitData { // TODO: Can skip parts of this. - let CircuitData { verifier_only, common, .. } = self.build(); - VerifierCircuitData { verifier_only, common } + let CircuitData { + verifier_only, + common, + .. + } = self.build(); + VerifierCircuitData { + verifier_only, + common, + } } } diff --git a/src/circuit_data.rs b/src/circuit_data.rs index 845bfe2f..ef0a74c6 100644 --- a/src/circuit_data.rs +++ b/src/circuit_data.rs @@ -128,7 +128,8 @@ impl CommonCircuitData { } pub fn constraint_degree(&self) -> usize { - self.gates.iter() + self.gates + .iter() .map(|g| g.0.degree()) .max() .expect("No gates?") diff --git a/src/field/cosets.rs b/src/field/cosets.rs index 5ef8566b..33f95242 100644 --- a/src/field/cosets.rs +++ b/src/field/cosets.rs @@ -2,19 +2,19 @@ use crate::field::field::Field; /// Finds a set of shifts that result in unique cosets for the multiplicative subgroup of size /// `2^subgroup_bits`. -pub(crate) fn get_unique_coset_shifts( - subgroup_size: usize, - num_shifts: usize, -) -> Vec { +pub(crate) fn get_unique_coset_shifts(subgroup_size: usize, num_shifts: usize) -> Vec { // From Lagrange's theorem. let num_cosets = (F::ORDER - 1) / (subgroup_size as u64); - assert!(num_shifts as u64 <= num_cosets, - "The subgroup does not have enough distinct cosets"); + assert!( + num_shifts as u64 <= num_cosets, + "The subgroup does not have enough distinct cosets" + ); // Let g be a generator of the entire multiplicative group. Let n be the order of the subgroup. // The subgroup can be written as . We can use g^0, ..., g^(num_shifts - 1) as our // shifts, since g^i are distinct cosets provided i < |F*| / n, which we checked. - F::MULTIPLICATIVE_GROUP_GENERATOR.powers() + F::MULTIPLICATIVE_GROUP_GENERATOR + .powers() .take(num_shifts) .collect() } @@ -45,7 +45,8 @@ mod tests { let coset = F::cyclic_subgroup_coset_known_order(generator, shift, subgroup_size); assert!( coset.into_iter().all(|x| union.insert(x)), - "Duplicate element!"); + "Duplicate element!" + ); } } } diff --git a/src/field/crandall_field.rs b/src/field/crandall_field.rs index 55e529a8..ae5dd89e 100644 --- a/src/field/crandall_field.rs +++ b/src/field/crandall_field.rs @@ -1,5 +1,5 @@ -use std::fmt::{Debug, Display, Formatter}; use std::fmt; +use std::fmt::{Debug, Display, Formatter}; use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign}; use num::Integer; @@ -123,11 +123,7 @@ impl Field for CrandallField { } } - let inverse = Self(if u == 1 { - b - } else { - c - }); + let inverse = Self(if u == 1 { b } else { c }); // Should change to debug_assert_eq; using assert_eq as an extra precaution for now until // we're more confident the impl is correct. diff --git a/src/field/fft.rs b/src/field/fft.rs index 05102374..fe3d2117 100644 --- a/src/field/fft.rs +++ b/src/field/fft.rs @@ -62,9 +62,8 @@ pub(crate) fn ifft_with_precomputation_power_of_2( let n_inv = F::from_canonical_usize(n).try_inverse().unwrap(); let PolynomialValues { values } = poly; - let PolynomialValues { values: mut result } = fft_with_precomputation_power_of_2( - PolynomialCoeffs { coeffs: values }, - precomputation); + let PolynomialValues { values: mut result } = + fft_with_precomputation_power_of_2(PolynomialCoeffs { coeffs: values }, precomputation); // We reverse all values except the first, and divide each by n. result[0] = result[0] * n_inv; @@ -155,11 +154,11 @@ pub(crate) fn coset_ifft(poly: PolynomialValues, shift: F) -> Polyn #[cfg(test)] mod tests { - use crate::util::{log2_ceil, log2_strict}; - use crate::field::fft::{ifft, fft}; - use crate::polynomial::polynomial::{PolynomialCoeffs, PolynomialValues}; - use crate::field::field::Field; use crate::field::crandall_field::CrandallField; + use crate::field::fft::{fft, ifft}; + use crate::field::field::Field; + use crate::polynomial::polynomial::{PolynomialCoeffs, PolynomialValues}; + use crate::util::{log2_ceil, log2_strict}; #[test] fn fft_and_ifft() { @@ -195,7 +194,9 @@ mod tests { evaluate_naive_power_of_2(&coefficients_padded) } - fn evaluate_naive_power_of_2(coefficients: &PolynomialCoeffs) -> PolynomialValues { + fn evaluate_naive_power_of_2( + coefficients: &PolynomialCoeffs, + ) -> PolynomialValues { let degree = coefficients.len(); let degree_pow = log2_strict(degree); diff --git a/src/field/field.rs b/src/field/field.rs index 869b3f6e..cfadfbe6 100644 --- a/src/field/field.rs +++ b/src/field/field.rs @@ -1,28 +1,30 @@ -use std::fmt::{Debug, Display}; -use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign}; -use rand::Rng; -use rand::rngs::OsRng; use crate::util::bits_u64; +use rand::rngs::OsRng; +use rand::Rng; +use std::fmt::{Debug, Display}; use std::hash::Hash; +use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign}; /// A finite field with prime order less than 2^64. -pub trait Field: 'static -+ Copy -+ Eq -+ Hash -+ Neg -+ Add -+ AddAssign -+ Sub -+ SubAssign -+ Mul -+ MulAssign -+ Div -+ DivAssign -+ Debug -+ Display -+ Send -+ Sync { +pub trait Field: + 'static + + Copy + + Eq + + Hash + + Neg + + Add + + AddAssign + + Sub + + SubAssign + + Mul + + MulAssign + + Div + + DivAssign + + Debug + + Display + + Send + + Sync +{ const ZERO: Self; const ONE: Self; const TWO: Self; @@ -92,7 +94,9 @@ pub trait Field: 'static assert!(n_power <= Self::TWO_ADICITY); let base = Self::POWER_OF_TWO_GENERATOR; // TODO: Just repeated squaring should be a bit faster, to avoid conditionals. - base.exp(Self::from_canonical_u64(1u64 << (Self::TWO_ADICITY - n_power))) + base.exp(Self::from_canonical_u64( + 1u64 << (Self::TWO_ADICITY - n_power), + )) } /// Computes a multiplicative subgroup whose order is known in advance. @@ -109,9 +113,7 @@ pub trait Field: 'static /// Computes a coset of a multiplicative subgroup whose order is known in advance. fn cyclic_subgroup_coset_known_order(generator: Self, shift: Self, order: usize) -> Vec { let subgroup = Self::cyclic_subgroup_known_order(generator, order); - subgroup.into_iter() - .map(|x| x * shift) - .collect() + subgroup.into_iter().map(|x| x * shift).collect() } fn to_canonical_u64(&self) -> u64; @@ -144,7 +146,10 @@ pub trait Field: 'static } fn powers(&self) -> Powers { - Powers { base: *self, current: Self::ONE } + Powers { + base: *self, + current: Self::ONE, + } } fn rand_from_rng(rng: &mut R) -> Self { diff --git a/src/field/field_testing.rs b/src/field/field_testing.rs index 1a852b73..d070cb8d 100644 --- a/src/field/field_testing.rs +++ b/src/field/field_testing.rs @@ -55,7 +55,6 @@ pub fn test_inputs(modulus: u64, word_bits: usize) -> Vec { // .collect() } - /// Apply the unary functions `op` and `expected_op` /// coordinate-wise to the inputs from `test_inputs(modulus, /// word_bits)` and panic if the two resulting vectors differ. @@ -70,18 +69,18 @@ pub fn run_unaryop_test_cases( ExpectedOp: Fn(u64) -> u64, { let inputs = test_inputs(modulus, word_bits); - let expected: Vec<_> = inputs.iter() - .map(|&x| expected_op(x)) - .collect(); + let expected: Vec<_> = inputs.iter().map(|&x| expected_op(x)).collect(); let output: Vec<_> = inputs .iter() .map(|&x| op(F::from_canonical_u64(x)).to_canonical_u64()) .collect(); // Compare expected outputs with actual outputs for i in 0..inputs.len() { - assert_eq!(output[i], expected[i], - "Expected {}, got {} for input {}", - expected[i], output[i], inputs[i]); + assert_eq!( + output[i], expected[i], + "Expected {}, got {} for input {}", + expected[i], output[i], inputs[i] + ); } } @@ -106,7 +105,8 @@ pub fn run_binaryop_test_cases( // Iterator over inputs rotated right by i places. Since // cycle().skip(i) rotates left by i, we need to rotate by // n_input_elts - i. - let shifted_inputs: Vec<_> = inputs.iter() + let shifted_inputs: Vec<_> = inputs + .iter() .cycle() .skip(inputs.len() - i) .take(inputs.len()) @@ -119,15 +119,21 @@ pub fn run_binaryop_test_cases( .map(|(x, y)| expected_op(x.clone(), y.clone())) .collect(); - let output: Vec<_> = inputs.iter().zip(shifted_inputs.clone()).map(|(&x, &y)| { - op(F::from_canonical_u64(x), F::from_canonical_u64(y)).to_canonical_u64() - }).collect(); + let output: Vec<_> = inputs + .iter() + .zip(shifted_inputs.clone()) + .map(|(&x, &y)| { + op(F::from_canonical_u64(x), F::from_canonical_u64(y)).to_canonical_u64() + }) + .collect(); // Compare expected outputs with actual outputs for i in 0..inputs.len() { - assert_eq!(output[i], expected[i], - "On inputs {} . {}, expected {} but got {}", - inputs[i], shifted_inputs[i], expected[i], output[i]); + assert_eq!( + output[i], expected[i], + "On inputs {} . {}, expected {} but got {}", + inputs[i], shifted_inputs[i], expected[i], output[i] + ); } } } @@ -146,57 +152,77 @@ macro_rules! test_arithmetic { #[test] fn arithmetic_addition() { let modulus = <$field>::ORDER; - crate::field::field_testing::run_binaryop_test_cases(modulus, WORD_BITS, <$field>::add, |x, y| { - let (z, over) = x.overflowing_add(y); - if over { - z.overflowing_sub(modulus).0 - } else if z >= modulus { - z - modulus - } else { - z - } - }) + crate::field::field_testing::run_binaryop_test_cases( + modulus, + WORD_BITS, + <$field>::add, + |x, y| { + let (z, over) = x.overflowing_add(y); + if over { + z.overflowing_sub(modulus).0 + } else if z >= modulus { + z - modulus + } else { + z + } + }, + ) } #[test] fn arithmetic_subtraction() { let modulus = <$field>::ORDER; - crate::field::field_testing::run_binaryop_test_cases(modulus, WORD_BITS, <$field>::sub, |x, y| { - if x >= y { - x - y - } else { - &modulus - y + x - } - }) + crate::field::field_testing::run_binaryop_test_cases( + modulus, + WORD_BITS, + <$field>::sub, + |x, y| { + if x >= y { + x - y + } else { + &modulus - y + x + } + }, + ) } #[test] fn arithmetic_negation() { let modulus = <$field>::ORDER; - crate::field::field_testing::run_unaryop_test_cases(modulus, WORD_BITS, <$field>::neg, |x| { - if x == 0 { - 0 - } else { - modulus - x - } - }) + crate::field::field_testing::run_unaryop_test_cases( + modulus, + WORD_BITS, + <$field>::neg, + |x| { + if x == 0 { + 0 + } else { + modulus - x + } + }, + ) } #[test] fn arithmetic_multiplication() { let modulus = <$field>::ORDER; - crate::field::field_testing::run_binaryop_test_cases(modulus, WORD_BITS, <$field>::mul, |x, y| { - ((x as u128) * (y as u128) % (modulus as u128)) as u64 - }) + crate::field::field_testing::run_binaryop_test_cases( + modulus, + WORD_BITS, + <$field>::mul, + |x, y| ((x as u128) * (y as u128) % (modulus as u128)) as u64, + ) } #[test] fn arithmetic_square() { let modulus = <$field>::ORDER; crate::field::field_testing::run_unaryop_test_cases( - modulus, WORD_BITS, + modulus, + WORD_BITS, |x: $field| x.square(), - |x| ((x as u128) * (x as u128) % (modulus as u128)) as u64) + |x| ((x as u128) * (x as u128) % (modulus as u128)) as u64, + ) } // #[test] diff --git a/src/field/mod.rs b/src/field/mod.rs index 04db57a2..9f58ef08 100644 --- a/src/field/mod.rs +++ b/src/field/mod.rs @@ -1,7 +1,7 @@ -pub mod crandall_field; -pub mod field; -pub mod fft; pub(crate) mod cosets; +pub mod crandall_field; +pub mod fft; +pub mod field; #[cfg(test)] mod field_testing; diff --git a/src/fri.rs b/src/fri.rs index 2275a362..a67bc93a 100644 --- a/src/fri.rs +++ b/src/fri.rs @@ -1,10 +1,13 @@ +use crate::field::fft::fft; use crate::field::field::Field; use crate::hash::{compress, hash_n_to_hash}; +use crate::merkle_proofs::verify_merkle_proof; +use crate::merkle_tree::MerkleTree; use crate::plonk_challenger::Challenger; use crate::polynomial::polynomial::{PolynomialCoeffs, PolynomialValues}; -use crate::proof::{Hash, FriProof}; -use crate::field::fft::fft; -use crate::gadgets::merkle_proofs::MerkleTree; +use crate::proof::{FriEvaluations, FriMerkleProofs, FriProof, FriQueryRound, Hash}; +use crate::util::log2_strict; +use std::cmp::min; /// Somewhat arbitrary. Smaller values will increase delta, but with diminishing returns, /// while increasing L, potentially requiring more challenge points. @@ -23,7 +26,8 @@ struct FriConfig { /// then the final domain will have size `2^(n-reduction_count)`. reduction_count: usize, - rate_bits: usize, + /// Number of query rounds to perform. + num_query_rounds: usize, } fn fri_delta(rate_log: usize, conjecture: bool) -> f64 { @@ -50,20 +54,24 @@ fn fri_l(codeword_len: usize, rate_log: usize, conjecture: bool) -> f64 { } // TODO: Different arity + PoW. -/// Performs a FRI round. -fn fri_round( +/// Builds a FRI proof. +fn fri_proof( + // Coefficients of the polynomial on which the LDT is performed. + // Only the first `1/rate_bits` coefficients are non-zero. polynomial_coeffs: &PolynomialCoeffs, + // Evaluation of the polynomial on the large domain. polynomial_values: &PolynomialValues, challenger: &mut Challenger, config: &FriConfig, ) -> FriProof { let n = polynomial_values.values.len(); - assert_eq!( - polynomial_coeffs.coeffs.len(), - n - ); - let mut trees = vec![MerkleTree::new(polynomial_values.values.iter().map(|&v| vec![v]).collect())]; - let mut root = trees.last().unwrap().root; + assert_eq!(polynomial_coeffs.coeffs.len(), n); + + let mut trees = vec![MerkleTree::new( + polynomial_values.values.iter().map(|&v| vec![v]).collect(), + true, + )]; + let mut root = trees[0].root; let mut coeffs = polynomial_coeffs.clone(); let mut values; @@ -72,6 +80,7 @@ fn fri_round( // Commit phase for _ in 0..config.reduction_count { let beta = challenger.get_challenge(); + // P(x) = P_0(x^2) + xP_1(x^2) becomes P_0(x) + beta*P_1(x) coeffs = PolynomialCoeffs::new( coeffs .coeffs @@ -79,30 +88,185 @@ fn fri_round( .map(|chunk| chunk[0] + beta * chunk[1]) .collect::>(), ); - values = fft(coeffs.clone().lde(config.rate_bits)); + values = fft(coeffs.clone()); - let tree = MerkleTree::new(values.values.iter().map(|&v| vec![v]).collect()); + let tree = MerkleTree::new(values.values.iter().map(|&v| vec![v]).collect(), true); challenger.observe_hash(&tree.root); trees.push(tree); } // Query phase - let mut merkle_proofs = Vec::new(); - let mut evals = Vec::new(); - for i in 0..config.reduction_count { + let mut query_round_proofs = Vec::new(); + for _ in 0..config.num_query_rounds { + let mut merkle_proofs = FriMerkleProofs { proofs: Vec::new() }; + let mut evals = FriEvaluations { + first_layer: (F::ZERO, F::ZERO), + rest: Vec::new(), + }; + // TODO: Challenger doesn't change between query rounds, so x is always the same. + // Once PoW is added, this should be fixed. let x = challenger.get_challenge(); - let x_index = (x.to_canonical_u64() as usize) % n; - let n2 = n>>1; - evals.extend(std::array::IntoIter::new([polynomial_values.values[x_index], polynomial_values.values[n2 + x_index]])); - merkle_proofs.extend(std::array::IntoIter::new([trees[i].prove(x_index), trees[i].prove(n2 + x_index)])); + let mut domain_size = n; + let mut x_index = x.to_canonical_u64() as usize; + for i in 0..config.reduction_count { + let domain_size2 = domain_size >> 1; + x_index %= domain_size; + let minus_x_index = (domain_size2 + x_index) % domain_size; + if i == 0 { + // For the first layer, we need to send the evaluation at `x` and `-x`. + evals.first_layer = (trees[i].get(x_index)[0], trees[i].get(minus_x_index)[0]); + } else { + // For the other layers, we only need to send the `-x`, the one at `x` can be inferred + // by the verifier. See the `compute_evaluation` function. + evals.rest.push(trees[i].get(minus_x_index)[0]); + } + merkle_proofs + .proofs + .push((trees[i].prove(x_index), trees[i].prove(minus_x_index))); + + domain_size = domain_size2; + } + query_round_proofs.push(FriQueryRound { + merkle_proofs, + evals, + }); } FriProof { commit_phase_merkle_roots: trees.iter().map(|t| t.root).collect(), initial_merkle_proofs: vec![], - intermediate_merkle_proofs: merkle_proofs, - final_poly: coeffs + query_round_proofs, + final_poly: coeffs, } - } +/// Computes P'(x^2) from P_even(x) and P_odd(x), where P' is the FRI reduced polynomial, +/// P_even is the even coefficients polynomial and P_off is the odd coefficients polynomial. +fn compute_evaluation(x: F, last_e_x: F, last_e_x_minus: F, beta: F) -> F { + // P(x) = P_0(x^2) + xP_1(x^2) + // P'(x^2) = P_0(x^2) + beta*P_1(x^2) + // P'(x^2) = ((P(x)+P(-x))/2) + beta*((P(x)-P(-x))/(2x) + (last_e_x + last_e_x_minus) / F::TWO + beta * (last_e_x - last_e_x_minus) / (F::TWO * x) +} + +fn verify_fri_proof( + proof: &FriProof, + challenger: &mut Challenger, + config: &FriConfig, +) -> Option<()> { + // Size of the LDE domain. + let n = proof.final_poly.len() << config.reduction_count; + + // Recover the random betas used in the FRI reductions. + let betas = proof.commit_phase_merkle_roots[..proof.commit_phase_merkle_roots.len() - 1] + .iter() + .map(|root| { + challenger.observe_hash(root); + challenger.get_challenge() + }) + .collect::>(); + challenger.observe_hash(proof.commit_phase_merkle_roots.last().unwrap()); + + // Check that parameters are coherent. + assert_eq!(config.num_query_rounds, proof.query_round_proofs.len()); + assert!(config.reduction_count > 0); + + for round in 0..config.num_query_rounds { + let round_proof = &proof.query_round_proofs[round]; + let mut e_xs = Vec::new(); + let x = challenger.get_challenge(); + let mut domain_size = n; + let mut x_index = x.to_canonical_u64() as usize; + // `subgroup_x` is `subgroup[x_index]`, i.e., the actual field element in the domain. + let mut subgroup_x = F::primitive_root_of_unity(log2_strict(n)).exp_usize(x_index % n); + for i in 0..config.reduction_count { + x_index %= domain_size; + let domain_size2 = domain_size >> 1; + let minus_x_index = (domain_size2 + x_index) % domain_size; + let (e_x, e_x_minus, merkle_proof, merkle_proof_minus) = if i == 0 { + let (e_x, e_x_minus) = round_proof.evals.first_layer; + let (merkle_proof, merkle_proof_minus) = &round_proof.merkle_proofs.proofs[i]; + e_xs.push((e_x, e_x_minus)); + (e_x, e_x_minus, merkle_proof, merkle_proof_minus) + } else { + let (last_e_x, last_e_x_minus) = e_xs[i - 1]; + let e_x = compute_evaluation(subgroup_x, last_e_x, last_e_x_minus, betas[i - 1]); + let e_x_minus = round_proof.evals.rest[i - 1]; + let (merkle_proof, merkle_proof_minus) = &round_proof.merkle_proofs.proofs[i]; + e_xs.push((e_x, e_x_minus)); + (e_x, e_x_minus, merkle_proof, merkle_proof_minus) + }; + (verify_merkle_proof( + vec![e_x], + x_index, + proof.commit_phase_merkle_roots[i], + merkle_proof, + true, + ) + .is_some() + && verify_merkle_proof( + vec![e_x_minus], + minus_x_index, + proof.commit_phase_merkle_roots[i], + merkle_proof_minus, + true, + ) + .is_some()) + .then(|| ())?; + if i > 0 { + subgroup_x = subgroup_x.square(); + } + domain_size = domain_size2; + } + let (last_e_x, last_e_x_minus) = e_xs[config.reduction_count - 1]; + let purported_eval = compute_evaluation( + subgroup_x, + last_e_x, + last_e_x_minus, + betas[config.reduction_count - 1], + ); + // Final check of FRI. After all the reduction, we check that the final polynomial is equal + // to the one sent by the prover. + (proof.final_poly.eval(subgroup_x.square()) == purported_eval).then(|| ())?; + } + Some(()) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::field::crandall_field::CrandallField; + use crate::field::fft::ifft; + + fn test_fri(degree: usize, rate_bits: usize, reduction_count: usize, num_query_rounds: usize) { + type F = CrandallField; + + let n = degree; + let evals = PolynomialValues::new((0..n).map(|_| F::rand()).collect()); + let lde = evals.clone().lde(rate_bits); + let config = FriConfig { + reduction_count, + num_query_rounds, + proof_of_work_bits: 0, + reduction_arity_bits: Vec::new(), + }; + let mut challenger = Challenger::new(); + let proof = fri_proof(&ifft(lde.clone()), &lde, &mut challenger, &config); + + let mut challenger = Challenger::new(); + assert!(verify_fri_proof(&proof, &mut challenger, &config).is_some()); + } + + #[test] + fn test_fri_multi_params() { + for degree_log in 1..6 { + for rate_bits in 0..4 { + for reduction_count in 1..=(degree_log + rate_bits) { + for num_query_round in 0..4 { + test_fri(1 << degree_log, rate_bits, reduction_count, num_query_round); + } + } + } + } + } +} diff --git a/src/gadgets/arithmetic.rs b/src/gadgets/arithmetic.rs index 7c9076da..2a8700e7 100644 --- a/src/gadgets/arithmetic.rs +++ b/src/gadgets/arithmetic.rs @@ -1,7 +1,7 @@ use crate::circuit_builder::CircuitBuilder; use crate::field::field::Field; -use crate::target::Target; use crate::gates::arithmetic::ArithmeticGate; +use crate::target::Target; use crate::wire::Wire; impl CircuitBuilder { @@ -22,10 +22,22 @@ impl CircuitBuilder { let gate = self.add_gate(ArithmeticGate::new(), vec![F::ONE, F::ONE]); - let wire_multiplicand_0 = Wire { gate, input: ArithmeticGate::WIRE_MULTIPLICAND_0 }; - let wire_multiplicand_1 = Wire { gate, input: ArithmeticGate::WIRE_MULTIPLICAND_1 }; - let wire_addend = Wire { gate, input: ArithmeticGate::WIRE_ADDEND }; - let wire_output = Wire { gate, input: ArithmeticGate::WIRE_OUTPUT }; + let wire_multiplicand_0 = Wire { + gate, + input: ArithmeticGate::WIRE_MULTIPLICAND_0, + }; + let wire_multiplicand_1 = Wire { + gate, + input: ArithmeticGate::WIRE_MULTIPLICAND_1, + }; + let wire_addend = Wire { + gate, + input: ArithmeticGate::WIRE_ADDEND, + }; + let wire_output = Wire { + gate, + input: ArithmeticGate::WIRE_OUTPUT, + }; self.route(x, Target::Wire(wire_multiplicand_0)); self.route(one, Target::Wire(wire_multiplicand_1)); diff --git a/src/gadgets/hash.rs b/src/gadgets/hash.rs index 20b26244..14cf0aca 100644 --- a/src/gadgets/hash.rs +++ b/src/gadgets/hash.rs @@ -11,31 +11,44 @@ use crate::wire::Wire; impl CircuitBuilder { pub fn permute(&mut self, inputs: [Target; 12]) -> [Target; 12] { let zero = self.zero(); - let gate = self.add_gate_no_constants( - GMiMCGate::::with_automatic_constants()); + let gate = + self.add_gate_no_constants(GMiMCGate::::with_automatic_constants()); // We don't want to swap any inputs, so set that wire to 0. let swap_wire = GMiMCGate::::WIRE_SWAP; - let swap_wire = Target::Wire(Wire { gate, input: swap_wire }); + let swap_wire = Target::Wire(Wire { + gate, + input: swap_wire, + }); self.route(zero, swap_wire); // The old accumulator wire doesn't matter, since we won't read the new accumulator wire. // We do have to set it to something though, so we'll arbitrary pick 0. let old_acc_wire = GMiMCGate::::WIRE_INDEX_ACCUMULATOR_OLD; - let old_acc_wire = Target::Wire(Wire { gate, input: old_acc_wire }); + let old_acc_wire = Target::Wire(Wire { + gate, + input: old_acc_wire, + }); self.route(zero, old_acc_wire); // Route input wires. for i in 0..12 { let in_wire = GMiMCGate::::wire_input(i); - let in_wire = Target::Wire(Wire { gate, input: in_wire }); + let in_wire = Target::Wire(Wire { + gate, + input: in_wire, + }); self.route(inputs[i], in_wire); } // Collect output wires. (0..12) - .map(|i| Target::Wire( - Wire { gate, input: GMiMCGate::::wire_output(i) })) + .map(|i| { + Target::Wire(Wire { + gate, + input: GMiMCGate::::wire_output(i), + }) + }) .collect::>() .try_into() .unwrap() diff --git a/src/gadgets/split_join.rs b/src/gadgets/split_join.rs index 21821706..e4dd2c78 100644 --- a/src/gadgets/split_join.rs +++ b/src/gadgets/split_join.rs @@ -1,9 +1,9 @@ +use crate::circuit_builder::CircuitBuilder; use crate::field::field::Field; use crate::generator::{SimpleGenerator, WitnessGenerator}; use crate::target::Target; use crate::wire::Wire; use crate::witness::PartialWitness; -use crate::circuit_builder::CircuitBuilder; impl CircuitBuilder { /// Split the given integer into a list of virtual advice targets, where each one represents a @@ -12,11 +12,7 @@ impl CircuitBuilder { /// Note that this only handles witness generation; it does not enforce that the decomposition /// is correct. The output should be treated as a "purported" decomposition which must be /// enforced elsewhere. - pub(crate) fn split_le_virtual( - &mut self, - integer: Target, - num_bits: usize, - ) -> Vec { + pub(crate) fn split_le_virtual(&mut self, integer: Target, num_bits: usize) -> Vec { let bit_targets = self.add_virtual_advice_targets(num_bits); split_le_generator::(integer, bit_targets.clone()); bit_targets @@ -37,9 +33,12 @@ pub fn split_le_generator_local_wires( integer_input_index: usize, bit_input_indices: &[usize], ) -> Box> { - let integer = Target::Wire( - Wire { gate, input: integer_input_index }); - let bits = bit_input_indices.iter() + let integer = Target::Wire(Wire { + gate, + input: integer_input_index, + }); + let bits = bit_input_indices + .iter() .map(|&input| Target::Wire(Wire { gate, input })) .collect(); Box::new(SplitGenerator { integer, bits }) @@ -66,8 +65,10 @@ impl SimpleGenerator for SplitGenerator { integer_value >>= 1; } - debug_assert_eq!(integer_value, 0, - "Integer too large to fit in given number of bits"); + debug_assert_eq!( + integer_value, 0, + "Integer too large to fit in given number of bits" + ); result } diff --git a/src/gates/arithmetic.rs b/src/gates/arithmetic.rs index b9211fb4..85cc8e08 100644 --- a/src/gates/arithmetic.rs +++ b/src/gates/arithmetic.rs @@ -1,9 +1,9 @@ use crate::circuit_builder::CircuitBuilder; -use crate::vars::{EvaluationTargets, EvaluationVars}; use crate::field::field::Field; use crate::gates::gate::{Gate, GateRef}; use crate::generator::{SimpleGenerator, WitnessGenerator}; use crate::target::Target; +use crate::vars::{EvaluationTargets, EvaluationVars}; use crate::wire::Wire; use crate::witness::PartialWitness; @@ -137,8 +137,7 @@ impl SimpleGenerator for ArithmeticGenerator { let multiplicand_1 = witness.get_wire(multiplicand_1_target); let addend = witness.get_wire(addend_target); - let output = self.const_0 * multiplicand_0 * multiplicand_1 - + self.const_1 * addend; + let output = self.const_0 * multiplicand_0 * multiplicand_1 + self.const_1 * addend; PartialWitness::singleton_wire(output_target, output) } diff --git a/src/gates/constant.rs b/src/gates/constant.rs index edd6e5ed..cf2b9e04 100644 --- a/src/gates/constant.rs +++ b/src/gates/constant.rs @@ -1,9 +1,9 @@ use crate::circuit_builder::CircuitBuilder; -use crate::vars::{EvaluationTargets, EvaluationVars}; use crate::field::field::Field; use crate::gates::gate::{Gate, GateRef}; use crate::generator::{SimpleGenerator, WitnessGenerator}; use crate::target::Target; +use crate::vars::{EvaluationTargets, EvaluationVars}; use crate::wire::Wire; use crate::witness::PartialWitness; @@ -83,7 +83,10 @@ impl SimpleGenerator for ConstantGenerator { } fn run_once(&self, _witness: &PartialWitness) -> PartialWitness { - let wire = Wire { gate: self.gate_index, input: ConstantGate::WIRE_OUTPUT }; + let wire = Wire { + gate: self.gate_index, + input: ConstantGate::WIRE_OUTPUT, + }; PartialWitness::singleton_target(Target::Wire(wire), self.constant) } } diff --git a/src/gates/gate.rs b/src/gates/gate.rs index e80b4e6b..58bd9dd9 100644 --- a/src/gates/gate.rs +++ b/src/gates/gate.rs @@ -2,10 +2,10 @@ use std::hash::{Hash, Hasher}; use std::sync::Arc; use crate::circuit_builder::CircuitBuilder; -use crate::vars::{EvaluationTargets, EvaluationVars}; use crate::field::field::Field; use crate::generator::WitnessGenerator; use crate::target::Target; +use crate::vars::{EvaluationTargets, EvaluationVars}; /// A custom gate. pub trait Gate: 'static + Send + Sync { diff --git a/src/gates/gmimc.rs b/src/gates/gmimc.rs index dad8b078..26a9efa5 100644 --- a/src/gates/gmimc.rs +++ b/src/gates/gmimc.rs @@ -168,8 +168,14 @@ impl SimpleGenerator for GMiMCGenerator { dep_input_indices.push(GMiMCGate::::WIRE_SWAP); dep_input_indices.push(GMiMCGate::::WIRE_INDEX_ACCUMULATOR_OLD); - dep_input_indices.into_iter() - .map(|input| Target::Wire(Wire { gate: self.gate_index, input })) + dep_input_indices + .into_iter() + .map(|input| { + Target::Wire(Wire { + gate: self.gate_index, + input, + }) + }) .collect() } @@ -177,10 +183,12 @@ impl SimpleGenerator for GMiMCGenerator { let mut result = PartialWitness::new(); let mut state = (0..W) - .map(|i| witness.get_wire(Wire { - gate: self.gate_index, - input: GMiMCGate::::wire_input(i), - })) + .map(|i| { + witness.get_wire(Wire { + gate: self.gate_index, + input: GMiMCGate::::wire_input(i), + }) + }) .collect::>(); let swap_value = witness.get_wire(Wire { @@ -205,7 +213,8 @@ impl SimpleGenerator for GMiMCGenerator { gate: self.gate_index, input: GMiMCGate::::WIRE_INDEX_ACCUMULATOR_NEW, }, - new_index_acc_value); + new_index_acc_value, + ); // Value that is implicitly added to each element. // See https://affine.group/2020/02/starkware-challenge @@ -219,7 +228,8 @@ impl SimpleGenerator for GMiMCGenerator { gate: self.gate_index, input: GMiMCGate::::wire_cubing_input(r), }, - cubing_input); + cubing_input, + ); let f = cubing_input.cube(); addition_buffer += f; state[active] -= f; @@ -232,7 +242,8 @@ impl SimpleGenerator for GMiMCGenerator { gate: self.gate_index, input: GMiMCGate::::wire_output(i), }, - state[i]); + state[i], + ); } result @@ -258,7 +269,7 @@ mod tests { type F = CrandallField; const R: usize = 101; let constants = Arc::new([F::TWO; R]); - type Gate = GMiMCGate::; + type Gate = GMiMCGate; let gate = Gate::with_constants(constants.clone()); let config = CircuitConfig { @@ -267,38 +278,51 @@ mod tests { ..Default::default() }; - let permutation_inputs = (0..W) - .map(F::from_canonical_usize) - .collect::>(); + let permutation_inputs = (0..W).map(F::from_canonical_usize).collect::>(); let mut witness = PartialWitness::new(); witness.set_wire( - Wire { gate: 0, input: Gate::WIRE_INDEX_ACCUMULATOR_OLD }, - F::from_canonical_usize(7)); + Wire { + gate: 0, + input: Gate::WIRE_INDEX_ACCUMULATOR_OLD, + }, + F::from_canonical_usize(7), + ); witness.set_wire( - Wire { gate: 0, input: Gate::WIRE_SWAP }, - F::ZERO); + Wire { + gate: 0, + input: Gate::WIRE_SWAP, + }, + F::ZERO, + ); for i in 0..W { witness.set_wire( - Wire { gate: 0, input: Gate::wire_input(i) }, - permutation_inputs[i]); + Wire { + gate: 0, + input: Gate::wire_input(i), + }, + permutation_inputs[i], + ); } let generators = gate.0.generators(0, &[], &[]); generate_partial_witness(&mut witness, &generators); - let expected_outputs: [F; W] = gmimc_permute_naive( - permutation_inputs.try_into().unwrap(), - constants); + let expected_outputs: [F; W] = + gmimc_permute_naive(permutation_inputs.try_into().unwrap(), constants); for i in 0..W { - let out = witness.get_wire( - Wire { gate: 0, input: Gate::wire_output(i) }); + let out = witness.get_wire(Wire { + gate: 0, + input: Gate::wire_output(i), + }); assert_eq!(out, expected_outputs[i]); } - let acc_new = witness.get_wire( - Wire { gate: 0, input: Gate::WIRE_INDEX_ACCUMULATOR_NEW }); + let acc_new = witness.get_wire(Wire { + gate: 0, + input: Gate::WIRE_INDEX_ACCUMULATOR_NEW, + }); assert_eq!(acc_new, F::from_canonical_usize(7 * 2)); } } diff --git a/src/gates/gmimc_eval.rs b/src/gates/gmimc_eval.rs index ddcc27bd..427559ab 100644 --- a/src/gates/gmimc_eval.rs +++ b/src/gates/gmimc_eval.rs @@ -1,9 +1,9 @@ use crate::circuit_builder::CircuitBuilder; -use crate::vars::{EvaluationTargets, EvaluationVars}; use crate::field::field::Field; use crate::gates::gate::{Gate, GateRef}; use crate::generator::{SimpleGenerator, WitnessGenerator}; use crate::target::Target; +use crate::vars::{EvaluationTargets, EvaluationVars}; use crate::witness::PartialWitness; /// Performs some arithmetic involved in the evaluation of GMiMC's constraint polynomials for one diff --git a/src/gates/noop.rs b/src/gates/noop.rs index d40091dc..fa261873 100644 --- a/src/gates/noop.rs +++ b/src/gates/noop.rs @@ -1,9 +1,9 @@ use crate::circuit_builder::CircuitBuilder; -use crate::vars::{EvaluationTargets, EvaluationVars}; use crate::field::field::Field; use crate::gates::gate::{Gate, GateRef}; use crate::generator::WitnessGenerator; use crate::target::Target; +use crate::vars::{EvaluationTargets, EvaluationVars}; /// A gate which takes a single constant parameter and outputs that value. pub struct NoopGate; diff --git a/src/gmimc.rs b/src/gmimc.rs index 547fe7b2..3b7d1e2c 100644 --- a/src/gmimc.rs +++ b/src/gmimc.rs @@ -15,11 +15,26 @@ pub(crate) fn gmimc_automatic_constants() -> [F; R] { constants } -pub fn gmimc_compress(a: [F; 4], b: [F; 4], constants: Arc<[F; R]>) -> [F; 4] { +pub fn gmimc_compress( + a: [F; 4], + b: [F; 4], + constants: Arc<[F; R]>, +) -> [F; 4] { // Sponge with r=8, c=4. - let state_0 = [a[0], a[1], a[2], a[3], b[0], - b[1], b[2], b[3], - F::ZERO, F::ZERO, F::ZERO, F::ZERO]; + let state_0 = [ + a[0], + a[1], + a[2], + a[3], + b[0], + b[1], + b[2], + b[3], + F::ZERO, + F::ZERO, + F::ZERO, + F::ZERO, + ]; let state_1 = gmimc_permute::(state_0, constants.clone()); [state_1[0], state_1[1], state_1[2], state_1[3]] } diff --git a/src/hash.rs b/src/hash.rs index e90cdb0a..97bb4821 100644 --- a/src/hash.rs +++ b/src/hash.rs @@ -4,12 +4,12 @@ use std::convert::TryInto; use rayon::prelude::*; +use crate::circuit_builder::CircuitBuilder; use crate::field::field::Field; use crate::gmimc::gmimc_permute_array; use crate::proof::{Hash, HashTarget}; -use crate::util::reverse_index_bits_in_place; -use crate::circuit_builder::CircuitBuilder; use crate::target::Target; +use crate::util::reverse_index_bits_in_place; pub(crate) const SPONGE_RATE: usize = 8; pub(crate) const SPONGE_CAPACITY: usize = 4; @@ -17,7 +17,109 @@ pub(crate) const SPONGE_WIDTH: usize = SPONGE_RATE + SPONGE_CAPACITY; pub const GMIMC_ROUNDS: usize = 101; /// This is the result of `gmimc_automatic_constants`; i.e. it's from ChaCha20 seeded with 0. -pub const GMIMC_CONSTANTS: [u64; GMIMC_ROUNDS] = [13080132715619999810, 8594738768332784433, 12896916466795114362, 1109962092924985887, 16216730424513838303, 10137062674532189451, 15292064468290167604, 17255573296743700660, 14827154243383347999, 2846171648262623971, 16246264665335217464, 14214208089399786945, 9667108688411000080, 6470857421371427314, 14103331941574951088, 11854816474757864855, 3498097497657653643, 7947235693333396721, 11110078702363612411, 16384314114341783099, 15404405914224921002, 14077880832148466479, 9555554663682579629, 13859595359622389547, 16859897326779206643, 17685474422023725021, 17858764736437889563, 9410011023624402450, 12495243630852222748, 12416945299436348089, 5776666812952701944, 6314421663507268983, 7402742472177291738, 982536713292517255, 17321168867539521172, 2934354895304883596, 10567510599683852824, 8135543734546633309, 116353493093565855, 8029688164312877009, 9003846638141970076, 7052445133185619935, 9645665433271393194, 5446430061585660707, 16770910636054378912, 17708360573237778662, 4661556288797079635, 11977051900536351292, 4378616569536950472, 3334807503157233344, 8019184736760206441, 2395043909056213726, 6558421058999795722, 11735894061922784518, 8143540539718733269, 5991753490174091591, 12235918792748480378, 2880312033996085535, 18224748117164817283, 18070411014966027790, 8156487614951798795, 10615269511128318233, 12489426406026437595, 5055279340584943685, 7231927320516917417, 2602078848371820415, 12445944370602567717, 3978905924297801117, 16711272946032085229, 10439032362290464320, 15110119873264383151, 821141790739535246, 11073536381779174375, 4866839313593360589, 13118391690850240703, 14527674975242150843, 7612751960041028847, 6808090908507673494, 6899703780195472329, 3664666286710282218, 783179505504239941, 8990689242729919931, 9646603556395461579, 7351246026916028004, 16970959815450893036, 15735726859844361172, 10347018222946250943, 12195545879691602738, 7423314197870213963, 14908016118492485461, 5840340123122280205, 17740311464247702688, 815306422036794512, 17456357369997417977, 6982651077270605698, 11970987325834369417, 8167785009370061651, 9483259820363401119, 954550221761525285, 10339565172077536587, 8651171085167737860]; +pub const GMIMC_CONSTANTS: [u64; GMIMC_ROUNDS] = [ + 13080132715619999810, + 8594738768332784433, + 12896916466795114362, + 1109962092924985887, + 16216730424513838303, + 10137062674532189451, + 15292064468290167604, + 17255573296743700660, + 14827154243383347999, + 2846171648262623971, + 16246264665335217464, + 14214208089399786945, + 9667108688411000080, + 6470857421371427314, + 14103331941574951088, + 11854816474757864855, + 3498097497657653643, + 7947235693333396721, + 11110078702363612411, + 16384314114341783099, + 15404405914224921002, + 14077880832148466479, + 9555554663682579629, + 13859595359622389547, + 16859897326779206643, + 17685474422023725021, + 17858764736437889563, + 9410011023624402450, + 12495243630852222748, + 12416945299436348089, + 5776666812952701944, + 6314421663507268983, + 7402742472177291738, + 982536713292517255, + 17321168867539521172, + 2934354895304883596, + 10567510599683852824, + 8135543734546633309, + 116353493093565855, + 8029688164312877009, + 9003846638141970076, + 7052445133185619935, + 9645665433271393194, + 5446430061585660707, + 16770910636054378912, + 17708360573237778662, + 4661556288797079635, + 11977051900536351292, + 4378616569536950472, + 3334807503157233344, + 8019184736760206441, + 2395043909056213726, + 6558421058999795722, + 11735894061922784518, + 8143540539718733269, + 5991753490174091591, + 12235918792748480378, + 2880312033996085535, + 18224748117164817283, + 18070411014966027790, + 8156487614951798795, + 10615269511128318233, + 12489426406026437595, + 5055279340584943685, + 7231927320516917417, + 2602078848371820415, + 12445944370602567717, + 3978905924297801117, + 16711272946032085229, + 10439032362290464320, + 15110119873264383151, + 821141790739535246, + 11073536381779174375, + 4866839313593360589, + 13118391690850240703, + 14527674975242150843, + 7612751960041028847, + 6808090908507673494, + 6899703780195472329, + 3664666286710282218, + 783179505504239941, + 8990689242729919931, + 9646603556395461579, + 7351246026916028004, + 16970959815450893036, + 15735726859844361172, + 10347018222946250943, + 12195545879691602738, + 7423314197870213963, + 14908016118492485461, + 5840340123122280205, + 17740311464247702688, + 815306422036794512, + 17456357369997417977, + 6982651077270605698, + 11970987325834369417, + 8167785009370061651, + 9483259820363401119, + 954550221761525285, + 10339565172077536587, + 8651171085167737860, +]; /// Controls the granularity of parallelization when building Merkle trees. I.e., we will try to /// split up the task into units of work, such that each unit involves hashing roughly this many @@ -160,18 +262,21 @@ pub(crate) fn merkle_root_bit_rev_order(mut vecs: Vec>) -> Hash pub(crate) fn merkle_root(vecs: Vec>) -> Hash { let elems_per_leaf = vecs[0].len(); let leaves_per_chunk = (ELEMS_PER_CHUNK / elems_per_leaf).next_power_of_two(); - let subtree_roots: Vec> = vecs.par_chunks(leaves_per_chunk) + let subtree_roots: Vec> = vecs + .par_chunks(leaves_per_chunk) .map(|chunk| merkle_root_inner(chunk.to_vec()).elements.to_vec()) .collect(); merkle_root_inner(subtree_roots) } pub(crate) fn merkle_root_inner(vecs: Vec>) -> Hash { - let mut hashes = vecs.into_iter() + let mut hashes = vecs + .into_iter() .map(|leaf_set| hash_or_noop(leaf_set)) .collect::>(); while hashes.len() > 1 { - hashes = hashes.chunks(2) + hashes = hashes + .chunks(2) .map(|pair| compress(pair[0], pair[1])) .collect(); } diff --git a/src/lib.rs b/src/lib.rs index 89762ead..18c1555a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,5 @@ pub mod circuit_builder; pub mod circuit_data; -pub mod vars; pub mod field; pub mod fri; pub mod gadgets; @@ -9,6 +8,7 @@ pub mod generator; pub mod gmimc; pub mod hash; pub mod merkle_proofs; +mod merkle_tree; pub mod plonk_challenger; pub mod plonk_common; pub mod polynomial; @@ -18,6 +18,7 @@ pub mod recursive_verifier; pub mod rescue; pub mod target; pub mod util; +pub mod vars; pub mod verifier; pub mod wire; pub mod witness; diff --git a/src/merkle_proofs.rs b/src/merkle_proofs.rs index 5f735e35..d9eb2516 100644 --- a/src/merkle_proofs.rs +++ b/src/merkle_proofs.rs @@ -1,12 +1,14 @@ use crate::circuit_builder::CircuitBuilder; use crate::field::field::Field; use crate::gates::gmimc::GMiMCGate; -use crate::hash::{compress, hash_or_noop}; use crate::hash::GMIMC_ROUNDS; +use crate::hash::{compress, hash_or_noop}; use crate::proof::{Hash, HashTarget}; use crate::target::Target; +use crate::util::reverse_index_bits; use crate::wire::Wire; +#[derive(Clone, Debug)] pub struct MerkleProof { /// The Merkle digest of each sibling subtree, staying from the bottommost layer. pub siblings: Vec>, @@ -23,18 +25,24 @@ pub(crate) fn verify_merkle_proof( leaf_data: Vec, leaf_index: usize, merkle_root: Hash, - proof: MerkleProof, -) -> bool { + proof: &MerkleProof, + reverse_bits: bool, +) -> Option<()> { + let index = if reverse_bits { + crate::util::reverse_bits(leaf_index, proof.siblings.len()) + } else { + leaf_index + }; let mut current_digest = hash_or_noop(leaf_data); - for (i, sibling_digest) in proof.siblings.into_iter().enumerate() { - let bit = (leaf_index >> i & 1) == 1; + for (i, &sibling_digest) in proof.siblings.iter().enumerate() { + let bit = (index >> i & 1) == 1; current_digest = if bit { compress(sibling_digest, current_digest) } else { compress(current_digest, sibling_digest) } } - current_digest == merkle_root + (current_digest == merkle_root).then(|| ()) } impl CircuitBuilder { @@ -55,24 +63,37 @@ impl CircuitBuilder { let mut acc_leaf_index = zero; for (bit, sibling) in purported_index_bits.into_iter().zip(proof.siblings) { - let gate = self.add_gate_no_constants( - GMiMCGate::::with_automatic_constants()); + let gate = self + .add_gate_no_constants(GMiMCGate::::with_automatic_constants()); let swap_wire = GMiMCGate::::WIRE_SWAP; - let swap_wire = Target::Wire(Wire { gate, input: swap_wire }); + let swap_wire = Target::Wire(Wire { + gate, + input: swap_wire, + }); self.generate_copy(bit, swap_wire); let old_acc_wire = GMiMCGate::::WIRE_INDEX_ACCUMULATOR_OLD; - let old_acc_wire = Target::Wire(Wire { gate, input: old_acc_wire }); + let old_acc_wire = Target::Wire(Wire { + gate, + input: old_acc_wire, + }); self.route(acc_leaf_index, old_acc_wire); let new_acc_wire = GMiMCGate::::WIRE_INDEX_ACCUMULATOR_NEW; - let new_acc_wire = Target::Wire(Wire { gate, input: new_acc_wire }); + let new_acc_wire = Target::Wire(Wire { + gate, + input: new_acc_wire, + }); acc_leaf_index = new_acc_wire; let input_wires = (0..12) - .map(|i| Target::Wire( - Wire { gate, input: GMiMCGate::::wire_input(i) })) + .map(|i| { + Target::Wire(Wire { + gate, + input: GMiMCGate::::wire_input(i), + }) + }) .collect::>(); for i in 0..4 { @@ -81,10 +102,16 @@ impl CircuitBuilder { self.route(zero, input_wires[8 + i]); } - state = HashTarget::from_vec((0..4) - .map(|i| Target::Wire( - Wire { gate, input: GMiMCGate::::wire_output(i) })) - .collect()) + state = HashTarget::from_vec( + (0..4) + .map(|i| { + Target::Wire(Wire { + gate, + input: GMiMCGate::::wire_output(i), + }) + }) + .collect(), + ) } self.assert_equal(acc_leaf_index, leaf_index); diff --git a/src/merkle_tree.rs b/src/merkle_tree.rs new file mode 100644 index 00000000..7ccf952d --- /dev/null +++ b/src/merkle_tree.rs @@ -0,0 +1,118 @@ +use crate::field::field::Field; +use crate::hash::{compress, hash_n_to_hash, hash_or_noop}; +use crate::merkle_proofs::MerkleProof; +use crate::proof::Hash; +use crate::util::{log2_strict, reverse_bits, reverse_index_bits_in_place}; + +#[derive(Clone, Debug)] +pub struct MerkleTree { + /// The data in the leaves of the Merkle tree. + pub leaves: Vec>, + + /// The layers of hashes in the tree. The first layer is the one at the bottom. + pub layers: Vec>>, + + /// The Merkle root. + pub root: Hash, + + /// If true, the indices are in bit-reversed form, so that the leaf at index `i` + /// contains the leaf originally at index `reverse_bits(i)`. + pub reverse_bits: bool, +} + +impl MerkleTree { + pub fn new(mut leaves: Vec>, reverse_bits: bool) -> Self { + if reverse_bits { + reverse_index_bits_in_place(&mut leaves); + } + let mut layers = vec![leaves + .iter() + .map(|l| hash_or_noop(l.clone())) + .collect::>()]; + while let Some(l) = layers.last() { + if l.len() == 1 { + break; + } + layers.push( + l.chunks(2) + .map(|chunk| compress(chunk[0], chunk[1])) + .collect::>(), + ); + } + let root = layers.pop().unwrap()[0]; + Self { + leaves, + layers, + root, + reverse_bits, + } + } + + pub fn get(&self, i: usize) -> &[F] { + let n = log2_strict(self.leaves.len()); + &self.leaves[if self.reverse_bits { + reverse_bits(i, n) + } else { + i + }] + } + + /// Create a Merkle proof from a leaf index. + pub fn prove(&self, leaf_index: usize) -> MerkleProof { + let index = if self.reverse_bits { + reverse_bits(leaf_index, log2_strict(self.leaves.len())) + } else { + leaf_index + }; + MerkleProof { + siblings: self + .layers + .iter() + .scan(index, |acc, layer| { + let index = *acc ^ 1; + *acc >>= 1; + Some(layer[index]) + }) + .collect(), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::field::crandall_field::CrandallField; + use crate::merkle_proofs::verify_merkle_proof; + use crate::polynomial::division::divide_by_z_h; + + #[test] + fn test_merkle_trees() { + type F = CrandallField; + + let n = 1 << 10; + let leaves: Vec> = (0..n) + .map(|_| (0..10).map(|_| F::rand()).collect()) + .collect(); + + let tree = MerkleTree::new(leaves.clone(), false); + for i in 0..n { + let proof = tree.prove(i); + assert!( + verify_merkle_proof(tree.leaves[i].clone(), i, tree.root, &proof, false).is_some() + ); + } + + let tree_reversed_bits = MerkleTree::new(leaves.clone(), true); + for i in 0..n { + let proof = tree_reversed_bits.prove(i); + assert!(verify_merkle_proof( + leaves[i].clone(), + i, + tree_reversed_bits.root, + &proof, + true + ) + .is_some()); + } + } +} diff --git a/src/plonk_challenger.rs b/src/plonk_challenger.rs index b745150d..0008278f 100644 --- a/src/plonk_challenger.rs +++ b/src/plonk_challenger.rs @@ -1,8 +1,8 @@ use crate::circuit_builder::CircuitBuilder; use crate::field::field::Field; -use crate::hash::{permute, SPONGE_WIDTH, SPONGE_RATE}; -use crate::target::Target; +use crate::hash::{permute, SPONGE_RATE, SPONGE_WIDTH}; use crate::proof::{Hash, HashTarget}; +use crate::target::Target; /// Observes prover messages, and generates challenges by hashing the transcript. #[derive(Clone)] @@ -130,10 +130,7 @@ impl RecursiveChallenger { self.observe_elements(&hash.elements) } - pub(crate) fn get_challenge( - &mut self, - builder: &mut CircuitBuilder, - ) -> Target { + pub(crate) fn get_challenge(&mut self, builder: &mut CircuitBuilder) -> Target { self.absorb_buffered_inputs(builder); if self.output_buffer.is_empty() { @@ -174,10 +171,7 @@ impl RecursiveChallenger { } /// Absorb any buffered inputs. After calling this, the input buffer will be empty. - fn absorb_buffered_inputs( - &mut self, - builder: &mut CircuitBuilder, - ) { + fn absorb_buffered_inputs(&mut self, builder: &mut CircuitBuilder) { for input_chunk in self.input_buffer.chunks(SPONGE_RATE) { // Overwrite the first r elements with the inputs. This differs from a standard sponge, // where we would xor or add in the inputs. This is a well-known variant, though, @@ -198,14 +192,14 @@ impl RecursiveChallenger { #[cfg(test)] mod tests { + use crate::circuit_builder::CircuitBuilder; use crate::circuit_data::CircuitConfig; use crate::field::crandall_field::CrandallField; + use crate::field::field::Field; use crate::generator::generate_partial_witness; use crate::plonk_challenger::{Challenger, RecursiveChallenger}; use crate::target::Target; - use crate::circuit_builder::CircuitBuilder; use crate::witness::PartialWitness; - use crate::field::field::Field; /// Tests for consistency between `Challenger` and `RecursiveChallenger`. #[test] @@ -237,8 +231,7 @@ mod tests { }; let mut builder = CircuitBuilder::::new(config); let mut recursive_challenger = RecursiveChallenger::new(&mut builder); - let mut recursive_outputs_per_round: Vec> = - Vec::new(); + let mut recursive_outputs_per_round: Vec> = Vec::new(); for (r, inputs) in inputs_per_round.iter().enumerate() { recursive_challenger.observe_elements(&builder.constants(inputs)); recursive_outputs_per_round.push( diff --git a/src/plonk_common.rs b/src/plonk_common.rs index 1c2deb02..e2227e56 100644 --- a/src/plonk_common.rs +++ b/src/plonk_common.rs @@ -1,8 +1,8 @@ use crate::circuit_builder::CircuitBuilder; use crate::field::field::Field; +use crate::gates::gate::GateRef; use crate::target::Target; use crate::vars::{EvaluationTargets, EvaluationVars}; -use crate::gates::gate::GateRef; /// Evaluates all gate constraints. /// @@ -18,8 +18,10 @@ pub fn evaluate_gate_constraints( for gate in gates { let gate_constraints = gate.0.eval_filtered(vars); for (i, c) in gate_constraints.into_iter().enumerate() { - debug_assert!(i < num_gate_constraints, - "num_constraints() gave too low of a number"); + debug_assert!( + i < num_gate_constraints, + "num_constraints() gave too low of a number" + ); constraints[i] += c; } } @@ -64,7 +66,8 @@ pub(crate) fn eval_l_1(n: usize, x: F) -> F { /// For each alpha in alphas, compute a reduction of the given terms using powers of alpha. pub(crate) fn reduce_with_powers_multi(terms: &[F], alphas: &[F]) -> Vec { - alphas.iter() + alphas + .iter() .map(|&alpha| reduce_with_powers(terms, alpha)) .collect() } diff --git a/src/polynomial/division.rs b/src/polynomial/division.rs index 9ee72d04..41d872d0 100644 --- a/src/polynomial/division.rs +++ b/src/polynomial/division.rs @@ -38,7 +38,8 @@ pub(crate) fn divide_by_z_h(mut a: PolynomialCoeffs, n: usize) -> P let denominators_inv = F::batch_multiplicative_inverse(&denominators); // Divide every element of `a_eval` by the corresponding denominator. // Then, `a_eval` is the evaluation of `a/Z_H` on `{g.w^i}`. - a_eval.values + a_eval + .values .iter_mut() .zip(denominators_inv.iter()) .for_each(|(x, &d)| { diff --git a/src/polynomial/polynomial.rs b/src/polynomial/polynomial.rs index cc9a4852..11949494 100644 --- a/src/polynomial/polynomial.rs +++ b/src/polynomial/polynomial.rs @@ -26,13 +26,8 @@ impl PolynomialValues { self.values.len() } - pub fn lde_multiple( - polys: Vec, - rate_bits: usize, - ) -> Vec { - polys.into_iter() - .map(|p| p.lde(rate_bits)) - .collect() + pub fn lde_multiple(polys: Vec, rate_bits: usize) -> Vec { + polys.into_iter().map(|p| p.lde(rate_bits)).collect() } pub fn lde(self, rate_bits: usize) -> Self { @@ -82,13 +77,15 @@ impl PolynomialCoeffs { .collect() } - pub fn lde_multiple( - polys: Vec, - rate_bits: usize, - ) -> Vec { - polys.into_iter() - .map(|p| p.lde(rate_bits)) - .collect() + pub fn eval(&self, x: F) -> F { + self.coeffs + .iter() + .rev() + .fold(F::ZERO, |acc, &c| acc * x + c) + } + + pub fn lde_multiple(polys: Vec, rate_bits: usize) -> Vec { + polys.into_iter().map(|p| p.lde(rate_bits)).collect() } pub(crate) fn lde(mut self, rate_bits: usize) -> Self { diff --git a/src/proof.rs b/src/proof.rs index 471a808e..35a64527 100644 --- a/src/proof.rs +++ b/src/proof.rs @@ -1,6 +1,7 @@ use crate::field::field::Field; +use crate::merkle_proofs::{MerkleProof, MerkleProofTarget}; +use crate::polynomial::polynomial::PolynomialCoeffs; use crate::target::Target; -use crate::merkle_proofs::{MerkleProofTarget, MerkleProof}; use std::convert::TryInto; /// Represents a ~256 bit hash output. @@ -12,7 +13,9 @@ pub struct Hash { impl Hash { pub(crate) fn from_vec(elements: Vec) -> Self { debug_assert!(elements.len() == 4); - Self { elements: elements.try_into().unwrap() } + Self { + elements: elements.try_into().unwrap(), + } } pub(crate) fn from_partial(mut elements: Vec) -> Self { @@ -20,7 +23,9 @@ impl Hash { while elements.len() < 4 { elements.push(F::ZERO); } - Self { elements: [elements[0], elements[1], elements[2], elements[3]] } + Self { + elements: [elements[0], elements[1], elements[2], elements[3]], + } } } @@ -32,7 +37,9 @@ pub struct HashTarget { impl HashTarget { pub(crate) fn from_vec(elements: Vec) -> Self { debug_assert!(elements.len() == 4); - Self { elements: elements.try_into().unwrap() } + Self { + elements: elements.try_into().unwrap(), + } } pub(crate) fn from_partial(mut elements: Vec, zero: Target) -> Self { @@ -40,7 +47,9 @@ impl HashTarget { while elements.len() < 4 { elements.push(zero); } - Self { elements: [elements[0], elements[1], elements[2], elements[3]] } + Self { + elements: [elements[0], elements[1], elements[2], elements[3]], + } } } @@ -74,13 +83,31 @@ pub struct ProofTarget { pub fri_proofs: Vec, } +// TODO: Implement FriEvaluationsTarget +#[derive(Debug)] +pub struct FriEvaluations { + pub first_layer: (F, F), + pub rest: Vec, +} + +// TODO: Implement FriEvaluationsTarget +pub struct FriMerkleProofs { + pub proofs: Vec<(MerkleProof, MerkleProof)>, +} + +// TODO: Implement FriQueryRoundTarget +pub struct FriQueryRound { + pub evals: FriEvaluations, + pub merkle_proofs: FriMerkleProofs, +} + pub struct FriProof { /// A Merkle root for each reduced polynomial in the commit phase. pub commit_phase_merkle_roots: Vec>, /// Merkle proofs for the original purported codewords, i.e. the subject of the LDT. pub initial_merkle_proofs: Vec>, - /// Merkle proofs for the reduced polynomials that were sent in the commit phase. - pub intermediate_merkle_proofs: Vec>, + /// Query rounds proofs + pub query_round_proofs: Vec>, /// The final polynomial in coefficient form. pub final_poly: PolynomialCoeffs, } diff --git a/src/prover.rs b/src/prover.rs index d4de3f5d..32735e17 100644 --- a/src/prover.rs +++ b/src/prover.rs @@ -4,17 +4,17 @@ use log::info; use rayon::prelude::*; use crate::circuit_data::{CommonCircuitData, ProverOnlyCircuitData}; -use crate::vars::EvaluationVars; use crate::field::fft::{fft, ifft}; use crate::field::field::Field; use crate::generator::generate_partial_witness; use crate::hash::merkle_root_bit_rev_order; use crate::plonk_challenger::Challenger; -use crate::plonk_common::{eval_l_1, reduce_with_powers_multi, evaluate_gate_constraints}; +use crate::plonk_common::{eval_l_1, evaluate_gate_constraints, reduce_with_powers_multi}; use crate::polynomial::division::divide_by_z_h; use crate::polynomial::polynomial::{PolynomialCoeffs, PolynomialValues}; use crate::proof::Proof; use crate::util::{transpose, transpose_poly_values}; +use crate::vars::EvaluationVars; use crate::wire::Wire; use crate::witness::PartialWitness; @@ -29,8 +29,10 @@ pub(crate) fn prove( let mut witness = inputs; info!("Running {} generators", prover_data.generators.len()); generate_partial_witness(&mut witness, &prover_data.generators); - info!("{:.3}s to generate witness", - start_witness.elapsed().as_secs_f32()); + info!( + "{:.3}s to generate witness", + start_witness.elapsed().as_secs_f32() + ); let config = common_data.config; let num_wires = config.num_wires; @@ -43,21 +45,27 @@ pub(crate) fn prove( .into_par_iter() .map(|i| compute_wire_lde(i, &witness, degree, config.rate_bits)) .collect::>(); - info!("{:.3}s to compute wire LDEs", - start_wire_ldes.elapsed().as_secs_f32()); + info!( + "{:.3}s to compute wire LDEs", + start_wire_ldes.elapsed().as_secs_f32() + ); // TODO: Could try parallelizing the transpose, or not doing it explicitly, instead having // merkle_root_bit_rev_order do it implicitly. let start_wire_transpose = Instant::now(); let wire_ldes_t = transpose_poly_values(wire_ldes); - info!("{:.3}s to transpose wire LDEs", - start_wire_transpose.elapsed().as_secs_f32()); + info!( + "{:.3}s to transpose wire LDEs", + start_wire_transpose.elapsed().as_secs_f32() + ); // TODO: Could avoid cloning if it's significant? let start_wires_root = Instant::now(); let wires_root = merkle_root_bit_rev_order(wire_ldes_t.clone()); - info!("{:.3}s to Merklize wire LDEs", - start_wires_root.elapsed().as_secs_f32()); + info!( + "{:.3}s to Merklize wire LDEs", + start_wires_root.elapsed().as_secs_f32() + ); let mut challenger = Challenger::new(); challenger.observe_hash(&wires_root); @@ -68,13 +76,17 @@ pub(crate) fn prove( let plonk_z_vecs = compute_zs(&common_data); let plonk_z_ldes = PolynomialValues::lde_multiple(plonk_z_vecs, config.rate_bits); let plonk_z_ldes_t = transpose_poly_values(plonk_z_ldes); - info!("{:.3}s to compute Z's and their LDEs", - start_plonk_z.elapsed().as_secs_f32()); + info!( + "{:.3}s to compute Z's and their LDEs", + start_plonk_z.elapsed().as_secs_f32() + ); let start_plonk_z_root = Instant::now(); let plonk_zs_root = merkle_root_bit_rev_order(plonk_z_ldes_t.clone()); - info!("{:.3}s to Merklize Z's", - start_plonk_z_root.elapsed().as_secs_f32()); + info!( + "{:.3}s to Merklize Z's", + start_plonk_z_root.elapsed().as_secs_f32() + ); challenger.observe_hash(&plonk_zs_root); @@ -86,9 +98,18 @@ pub(crate) fn prove( let start_vanishing_polys = Instant::now(); let vanishing_polys = compute_vanishing_polys( - common_data, prover_data, wire_ldes_t, plonk_z_ldes_t, beta, gamma, &alphas); - info!("{:.3}s to compute vanishing polys", - start_vanishing_polys.elapsed().as_secs_f32()); + common_data, + prover_data, + wire_ldes_t, + plonk_z_ldes_t, + beta, + gamma, + &alphas, + ); + info!( + "{:.3}s to compute vanishing polys", + start_vanishing_polys.elapsed().as_secs_f32() + ); // Compute the quotient polynomials, aka `t` in the Plonk paper. let quotient_polys_start = Instant::now(); @@ -98,23 +119,27 @@ pub(crate) fn prove( let quotient_poly_coeff = divide_by_z_h(vanishing_poly_coeff, degree); // Split t into degree-n chunks. let quotient_poly_coeff_chunks = quotient_poly_coeff.chunks(degree); - let quotient_poly_coeff_ldes = PolynomialCoeffs::lde_multiple( - quotient_poly_coeff_chunks, config.rate_bits); + let quotient_poly_coeff_ldes = + PolynomialCoeffs::lde_multiple(quotient_poly_coeff_chunks, config.rate_bits); let quotient_poly_chunk_ldes: Vec> = quotient_poly_coeff_ldes.into_par_iter().map(fft).collect(); all_quotient_poly_chunk_ldes.extend(quotient_poly_chunk_ldes); } - let quotient_polys_root = merkle_root_bit_rev_order( - transpose_poly_values(all_quotient_poly_chunk_ldes)); - info!("{:.3}s to compute quotient polys and their LDEs", - quotient_polys_start.elapsed().as_secs_f32()); + let quotient_polys_root = + merkle_root_bit_rev_order(transpose_poly_values(all_quotient_poly_chunk_ldes)); + info!( + "{:.3}s to compute quotient polys and their LDEs", + quotient_polys_start.elapsed().as_secs_f32() + ); let openings = Vec::new(); // TODO let fri_proofs = Vec::new(); // TODO - info!("{:.3}s for overall witness & proof generation", - start_proof_gen.elapsed().as_secs_f32()); + info!( + "{:.3}s for overall witness & proof generation", + start_proof_gen.elapsed().as_secs_f32() + ); Proof { wires_root, @@ -150,28 +175,41 @@ fn compute_vanishing_polys( let num_checks = common_data.config.num_checks; let points = F::cyclic_subgroup_known_order(lde_gen, lde_size); - let values: Vec> = points.into_par_iter().enumerate().map(|(i, x)| { - let i_next = (i + 1) % lde_size; - let local_wires = &wire_ldes_t[i]; - let next_wires = &wire_ldes_t[i_next]; - let local_constants = &prover_data.constant_ldes_t[i]; - let next_constants = &prover_data.constant_ldes_t[i_next]; - let local_plonk_zs = &plonk_z_lde_t[i]; - let next_plonk_zs = &plonk_z_lde_t[i_next]; - let s_sigmas = &prover_data.sigma_ldes_t[i]; + let values: Vec> = points + .into_par_iter() + .enumerate() + .map(|(i, x)| { + let i_next = (i + 1) % lde_size; + let local_wires = &wire_ldes_t[i]; + let next_wires = &wire_ldes_t[i_next]; + let local_constants = &prover_data.constant_ldes_t[i]; + let next_constants = &prover_data.constant_ldes_t[i_next]; + let local_plonk_zs = &plonk_z_lde_t[i]; + let next_plonk_zs = &plonk_z_lde_t[i_next]; + let s_sigmas = &prover_data.sigma_ldes_t[i]; - debug_assert_eq!(local_wires.len(), common_data.config.num_wires); - debug_assert_eq!(local_plonk_zs.len(), num_checks); + debug_assert_eq!(local_wires.len(), common_data.config.num_wires); + debug_assert_eq!(local_plonk_zs.len(), num_checks); - let vars = EvaluationVars { - local_constants, - next_constants, - local_wires, - next_wires, - }; - compute_vanishing_poly_entry( - common_data, x, vars, local_plonk_zs, next_plonk_zs, s_sigmas, beta, gamma, alphas) - }).collect(); + let vars = EvaluationVars { + local_constants, + next_constants, + local_wires, + next_wires, + }; + compute_vanishing_poly_entry( + common_data, + x, + vars, + local_plonk_zs, + next_plonk_zs, + s_sigmas, + beta, + gamma, + alphas, + ) + }) + .collect(); transpose(&values) .into_iter() @@ -193,8 +231,8 @@ fn compute_vanishing_poly_entry( gamma: F, alphas: &[F], ) -> Vec { - let constraint_terms = evaluate_gate_constraints( - &common_data.gates, common_data.num_gate_constraints, vars); + let constraint_terms = + evaluate_gate_constraints(&common_data.gates, common_data.num_gate_constraints, vars); // The L_1(x) (Z(x) - 1) vanishing terms. let mut vanishing_z_1_terms = Vec::new(); @@ -223,7 +261,8 @@ fn compute_vanishing_poly_entry( vanishing_z_1_terms, vanishing_v_shift_terms, constraint_terms, - ].concat(); + ] + .concat(); reduce_with_powers_multi(&vanishing_terms, alphas) } @@ -239,7 +278,11 @@ fn compute_wire_lde( // wires, so some wire values will not be set. We can set these to any value; here we // arbitrary pick zero. Ideally we would verify that no constraints operate on these unset // wires, but that isn't trivial. - .map(|gate| witness.try_get_wire(Wire { gate, input }).unwrap_or(F::ZERO)) + .map(|gate| { + witness + .try_get_wire(Wire { gate, input }) + .unwrap_or(F::ZERO) + }) .collect(); PolynomialValues::new(wire_values).lde(rate_bits) } diff --git a/src/rescue.rs b/src/rescue.rs index f97e0782..88785a1e 100644 --- a/src/rescue.rs +++ b/src/rescue.rs @@ -7,41 +7,457 @@ const ROUNDS: usize = 10; const W: usize = 12; const MDS: [[u64; W]; W] = [ - [10760600708254618966, 16769767337539665921, 5534023221388089754, 2049638230143736946, 16140901062381928449, 2635249153041947502, 3074457345215605419, 11068046442776179508, 13835058053470224385, 6148914690431210838, 9223372035646816257, 1, ], - [5675921252705733081, 10760600708254618966, 16769767337539665921, 5534023221388089754, 2049638230143736946, 16140901062381928449, 2635249153041947502, 3074457345215605419, 11068046442776179508, 13835058053470224385, 6148914690431210838, 9223372035646816257, ], - [1317624576520973751, 5675921252705733081, 10760600708254618966, 16769767337539665921, 5534023221388089754, 2049638230143736946, 16140901062381928449, 2635249153041947502, 3074457345215605419, 11068046442776179508, 13835058053470224385, 6148914690431210838, ], - [15987178195121148178, 1317624576520973751, 5675921252705733081, 10760600708254618966, 16769767337539665921, 5534023221388089754, 2049638230143736946, 16140901062381928449, 2635249153041947502, 3074457345215605419, 11068046442776179508, 13835058053470224385, ], - [17293822566837780481, 15987178195121148178, 1317624576520973751, 5675921252705733081, 10760600708254618966, 16769767337539665921, 5534023221388089754, 2049638230143736946, 16140901062381928449, 2635249153041947502, 3074457345215605419, 11068046442776179508, ], - [3255307777287111620, 17293822566837780481, 15987178195121148178, 1317624576520973751, 5675921252705733081, 10760600708254618966, 16769767337539665921, 5534023221388089754, 2049638230143736946, 16140901062381928449, 2635249153041947502, 3074457345215605419, ], - [1024819115071868473, 3255307777287111620, 17293822566837780481, 15987178195121148178, 1317624576520973751, 5675921252705733081, 10760600708254618966, 16769767337539665921, 5534023221388089754, 2049638230143736946, 16140901062381928449, 2635249153041947502, ], - [9708812669101911849, 1024819115071868473, 3255307777287111620, 17293822566837780481, 15987178195121148178, 1317624576520973751, 5675921252705733081, 10760600708254618966, 16769767337539665921, 5534023221388089754, 2049638230143736946, 16140901062381928449, ], - [2767011610694044877, 9708812669101911849, 1024819115071868473, 3255307777287111620, 17293822566837780481, 15987178195121148178, 1317624576520973751, 5675921252705733081, 10760600708254618966, 16769767337539665921, 5534023221388089754, 2049638230143736946, ], - [878416384347315834, 2767011610694044877, 9708812669101911849, 1024819115071868473, 3255307777287111620, 17293822566837780481, 15987178195121148178, 1317624576520973751, 5675921252705733081, 10760600708254618966, 16769767337539665921, 5534023221388089754, ], - [17608255704416649217, 878416384347315834, 2767011610694044877, 9708812669101911849, 1024819115071868473, 3255307777287111620, 17293822566837780481, 15987178195121148178, 1317624576520973751, 5675921252705733081, 10760600708254618966, 16769767337539665921, ], - [15238614667590392076, 17608255704416649217, 878416384347315834, 2767011610694044877, 9708812669101911849, 1024819115071868473, 3255307777287111620, 17293822566837780481, 15987178195121148178, 1317624576520973751, 5675921252705733081, 10760600708254618966, ], + [ + 10760600708254618966, + 16769767337539665921, + 5534023221388089754, + 2049638230143736946, + 16140901062381928449, + 2635249153041947502, + 3074457345215605419, + 11068046442776179508, + 13835058053470224385, + 6148914690431210838, + 9223372035646816257, + 1, + ], + [ + 5675921252705733081, + 10760600708254618966, + 16769767337539665921, + 5534023221388089754, + 2049638230143736946, + 16140901062381928449, + 2635249153041947502, + 3074457345215605419, + 11068046442776179508, + 13835058053470224385, + 6148914690431210838, + 9223372035646816257, + ], + [ + 1317624576520973751, + 5675921252705733081, + 10760600708254618966, + 16769767337539665921, + 5534023221388089754, + 2049638230143736946, + 16140901062381928449, + 2635249153041947502, + 3074457345215605419, + 11068046442776179508, + 13835058053470224385, + 6148914690431210838, + ], + [ + 15987178195121148178, + 1317624576520973751, + 5675921252705733081, + 10760600708254618966, + 16769767337539665921, + 5534023221388089754, + 2049638230143736946, + 16140901062381928449, + 2635249153041947502, + 3074457345215605419, + 11068046442776179508, + 13835058053470224385, + ], + [ + 17293822566837780481, + 15987178195121148178, + 1317624576520973751, + 5675921252705733081, + 10760600708254618966, + 16769767337539665921, + 5534023221388089754, + 2049638230143736946, + 16140901062381928449, + 2635249153041947502, + 3074457345215605419, + 11068046442776179508, + ], + [ + 3255307777287111620, + 17293822566837780481, + 15987178195121148178, + 1317624576520973751, + 5675921252705733081, + 10760600708254618966, + 16769767337539665921, + 5534023221388089754, + 2049638230143736946, + 16140901062381928449, + 2635249153041947502, + 3074457345215605419, + ], + [ + 1024819115071868473, + 3255307777287111620, + 17293822566837780481, + 15987178195121148178, + 1317624576520973751, + 5675921252705733081, + 10760600708254618966, + 16769767337539665921, + 5534023221388089754, + 2049638230143736946, + 16140901062381928449, + 2635249153041947502, + ], + [ + 9708812669101911849, + 1024819115071868473, + 3255307777287111620, + 17293822566837780481, + 15987178195121148178, + 1317624576520973751, + 5675921252705733081, + 10760600708254618966, + 16769767337539665921, + 5534023221388089754, + 2049638230143736946, + 16140901062381928449, + ], + [ + 2767011610694044877, + 9708812669101911849, + 1024819115071868473, + 3255307777287111620, + 17293822566837780481, + 15987178195121148178, + 1317624576520973751, + 5675921252705733081, + 10760600708254618966, + 16769767337539665921, + 5534023221388089754, + 2049638230143736946, + ], + [ + 878416384347315834, + 2767011610694044877, + 9708812669101911849, + 1024819115071868473, + 3255307777287111620, + 17293822566837780481, + 15987178195121148178, + 1317624576520973751, + 5675921252705733081, + 10760600708254618966, + 16769767337539665921, + 5534023221388089754, + ], + [ + 17608255704416649217, + 878416384347315834, + 2767011610694044877, + 9708812669101911849, + 1024819115071868473, + 3255307777287111620, + 17293822566837780481, + 15987178195121148178, + 1317624576520973751, + 5675921252705733081, + 10760600708254618966, + 16769767337539665921, + ], + [ + 15238614667590392076, + 17608255704416649217, + 878416384347315834, + 2767011610694044877, + 9708812669101911849, + 1024819115071868473, + 3255307777287111620, + 17293822566837780481, + 15987178195121148178, + 1317624576520973751, + 5675921252705733081, + 10760600708254618966, + ], ]; const RESCUE_CONSTANTS: [[u64; W]; 20] = [ - [12050887499329086906, 1748247961703512657, 315780861775001585, 2827656358919812970, 13335864861236723579, 3010729529365640897, 8463534053828271146, 2528500966106598845, 8969871077123422281, 1002624930202741107, 599979829006456404, 4386170815218774254, ], - [5771413917591851532, 11946802620311685142, 4759792267858670262, 6879094914431255667, 3985911073214909073, 1542850118294175816, 5393560436452023029, 8331250756632997735, 3395511836281190608, 17601255793194446503, 12848459944475727152, 11995465655754698601, ], - [14063960046551560130, 14790209580166185143, 5509023472758717841, 1274395897760495573, 16719545989415697758, 17865948122414223407, 3919263713959798649, 5633741078654387163, 15665612362287352054, 3418834727998553015, 5324019631954832682, 17962066557010997431, ], - [3282193104189649752, 18423507935939999211, 9035104445528866459, 30842260240043277, 3896337933354935129, 6615548113269323045, 6625827707190475694, 6677757329269550670, 11419013193186889337, 17111888851716383760, 12075517898615128691, 8139844272075088233, ], - [8872892112814161072, 17529364346566228604, 7526576514327158912, 850359069964902700, 9679332912197531902, 10591229741059812071, 12759208863825924546, 14552519355635838750, 16066249893409806278, 11283035366525176262, 1047378652379935387, 17032498397644511356, ], - [2938626421478254042, 10375267398354586672, 13728514869380643947, 16707318479225743731, 9785828188762698567, 8610686976269299752, 5478372191917042178, 12716344455538470365, 9968276048553747246, 14746805727771473956, 4822070620124107028, 9901161649549513416, ], - [13458162407040644078, 4045792126424269312, 9709263167782315020, 2163173014916005515, 17079206331095671215, 2556388076102629669, 6582772486087242347, 1239959540200663058, 18268236910639895687, 12499012548657350745, 17213068585339946119, 7641451088868756688, ], - [14674555473338434116, 14624532976317185113, 13625541984298615970, 7612892294159054770, 12294028208969561574, 6067206081581804358, 5778082506883496792, 7389487446513884800, 12929525660730020877, 18244350162788654296, 15285920877034454694, 3640669683987215349, ], - [6737585134029996281, 1826890539455248546, 289376081355380231, 10782622161517803787, 12978425540147835172, 9828233103297278473, 16384075371934678711, 3187492301890791304, 12985433735185968457, 9470935291631377473, 16328323199113140151, 16218490552434224203, ], - [6188809977565251499, 18437718710937437067, 4530469469895539008, 9596355277372723349, 13602518824447658705, 8759976068576854281, 10504320064094929535, 3980760429843656150, 14609448298151012462, 5839843841558860609, 10283805260656050418, 7239168159249274821, ], - [3604243611640027441, 5237321927316578323, 5071861664926666316, 13025405632646149705, 3285281651566464074, 12121596060272825779, 1900602777802961569, 8122527981264852045, 6731303887159752901, 9197659817406857040, 844741616904786364, 14249777686667858094, ], - [8602844218963499297, 10133401373828451640, 11618292280328565166, 8828272598402499582, 4252246265076774689, 9760449011955070998, 10233981507028897480, 10427510555228840014, 1007817664531124790, 4465396600980659145, 7727267420665314215, 7904022788946844554, ], - [11418297156527169222, 15865399053509010196, 1727198235391450850, 16557095577717348672, 1524052121709169653, 14531367160053894310, 4071756280138432327, 10333204220115446291, 16584144375833061215, 12237566480526488368, 11090440024401607208, 18281335018830792766, ], - [16152169547074248135, 18338155611216027761, 15842640128213925612, 14687926435880145351, 13259626900273707210, 6187877366876303234, 10312881470701795438, 1924945292721719446, 2278209355262975917, 3250749056007953206, 11589006946114672195, 241829012299953928, ], - [11244459446597052449, 7319043416418482137, 8148526814449636806, 9054933038587901070, 550333919248348827, 5513167392062632770, 12644459803778263764, 9903621375535446226, 16390581784506871871, 14586524717888286021, 6975796306584548762, 5200407948555191573, ], - [2855794043288846965, 1259443213892506318, 6145351706926586935, 3853784494234324998, 5871277378086513850, 9414363368707862566, 11946957446931890832, 308083693687568600, 12712587722369770461, 6792392698104204991, 16465224002344550280, 10282380383506806095, ], - [12608209810104211593, 11808578423511814760, 16177950852717156460, 9394439296563712221, 12586575762376685187, 17703393198607870393, 9811861465513647715, 14126450959506560131, 12713673607080398908, 18301828072718562389, 11180556590297273821, 4451415492203885059, ], - [10465807219916311101, 1213997644391575261, 17672155373280862521, 1491206970207330736, 10977478805896263804, 13260961975618373124, 16060889403827043708, 3223573072465920682, 17624203443801796697, 10247205738678800822, 11100653267668698651, 14328592975764892571, ], - [6984072551318461094, 3416562710010527326, 12847783919251969270, 12223185134739244472, 12073170519625198198, 6221124633828606855, 17596623990006806590, 1153871693574764968, 2548851681903410721, 9823373270182377847, 16708030507924899244, 9619306826188519218, ], - [5842685042453818473, 12400879353954910914, 647112787845575111, 4893664959929687347, 3759391664155971284, 15871181179823725763, 3629377713951158273, 3439101502554162312, 8325686353010019444, 10630488935940555500, 3478529754946055748, 12681233130980545828, ], + [ + 12050887499329086906, + 1748247961703512657, + 315780861775001585, + 2827656358919812970, + 13335864861236723579, + 3010729529365640897, + 8463534053828271146, + 2528500966106598845, + 8969871077123422281, + 1002624930202741107, + 599979829006456404, + 4386170815218774254, + ], + [ + 5771413917591851532, + 11946802620311685142, + 4759792267858670262, + 6879094914431255667, + 3985911073214909073, + 1542850118294175816, + 5393560436452023029, + 8331250756632997735, + 3395511836281190608, + 17601255793194446503, + 12848459944475727152, + 11995465655754698601, + ], + [ + 14063960046551560130, + 14790209580166185143, + 5509023472758717841, + 1274395897760495573, + 16719545989415697758, + 17865948122414223407, + 3919263713959798649, + 5633741078654387163, + 15665612362287352054, + 3418834727998553015, + 5324019631954832682, + 17962066557010997431, + ], + [ + 3282193104189649752, + 18423507935939999211, + 9035104445528866459, + 30842260240043277, + 3896337933354935129, + 6615548113269323045, + 6625827707190475694, + 6677757329269550670, + 11419013193186889337, + 17111888851716383760, + 12075517898615128691, + 8139844272075088233, + ], + [ + 8872892112814161072, + 17529364346566228604, + 7526576514327158912, + 850359069964902700, + 9679332912197531902, + 10591229741059812071, + 12759208863825924546, + 14552519355635838750, + 16066249893409806278, + 11283035366525176262, + 1047378652379935387, + 17032498397644511356, + ], + [ + 2938626421478254042, + 10375267398354586672, + 13728514869380643947, + 16707318479225743731, + 9785828188762698567, + 8610686976269299752, + 5478372191917042178, + 12716344455538470365, + 9968276048553747246, + 14746805727771473956, + 4822070620124107028, + 9901161649549513416, + ], + [ + 13458162407040644078, + 4045792126424269312, + 9709263167782315020, + 2163173014916005515, + 17079206331095671215, + 2556388076102629669, + 6582772486087242347, + 1239959540200663058, + 18268236910639895687, + 12499012548657350745, + 17213068585339946119, + 7641451088868756688, + ], + [ + 14674555473338434116, + 14624532976317185113, + 13625541984298615970, + 7612892294159054770, + 12294028208969561574, + 6067206081581804358, + 5778082506883496792, + 7389487446513884800, + 12929525660730020877, + 18244350162788654296, + 15285920877034454694, + 3640669683987215349, + ], + [ + 6737585134029996281, + 1826890539455248546, + 289376081355380231, + 10782622161517803787, + 12978425540147835172, + 9828233103297278473, + 16384075371934678711, + 3187492301890791304, + 12985433735185968457, + 9470935291631377473, + 16328323199113140151, + 16218490552434224203, + ], + [ + 6188809977565251499, + 18437718710937437067, + 4530469469895539008, + 9596355277372723349, + 13602518824447658705, + 8759976068576854281, + 10504320064094929535, + 3980760429843656150, + 14609448298151012462, + 5839843841558860609, + 10283805260656050418, + 7239168159249274821, + ], + [ + 3604243611640027441, + 5237321927316578323, + 5071861664926666316, + 13025405632646149705, + 3285281651566464074, + 12121596060272825779, + 1900602777802961569, + 8122527981264852045, + 6731303887159752901, + 9197659817406857040, + 844741616904786364, + 14249777686667858094, + ], + [ + 8602844218963499297, + 10133401373828451640, + 11618292280328565166, + 8828272598402499582, + 4252246265076774689, + 9760449011955070998, + 10233981507028897480, + 10427510555228840014, + 1007817664531124790, + 4465396600980659145, + 7727267420665314215, + 7904022788946844554, + ], + [ + 11418297156527169222, + 15865399053509010196, + 1727198235391450850, + 16557095577717348672, + 1524052121709169653, + 14531367160053894310, + 4071756280138432327, + 10333204220115446291, + 16584144375833061215, + 12237566480526488368, + 11090440024401607208, + 18281335018830792766, + ], + [ + 16152169547074248135, + 18338155611216027761, + 15842640128213925612, + 14687926435880145351, + 13259626900273707210, + 6187877366876303234, + 10312881470701795438, + 1924945292721719446, + 2278209355262975917, + 3250749056007953206, + 11589006946114672195, + 241829012299953928, + ], + [ + 11244459446597052449, + 7319043416418482137, + 8148526814449636806, + 9054933038587901070, + 550333919248348827, + 5513167392062632770, + 12644459803778263764, + 9903621375535446226, + 16390581784506871871, + 14586524717888286021, + 6975796306584548762, + 5200407948555191573, + ], + [ + 2855794043288846965, + 1259443213892506318, + 6145351706926586935, + 3853784494234324998, + 5871277378086513850, + 9414363368707862566, + 11946957446931890832, + 308083693687568600, + 12712587722369770461, + 6792392698104204991, + 16465224002344550280, + 10282380383506806095, + ], + [ + 12608209810104211593, + 11808578423511814760, + 16177950852717156460, + 9394439296563712221, + 12586575762376685187, + 17703393198607870393, + 9811861465513647715, + 14126450959506560131, + 12713673607080398908, + 18301828072718562389, + 11180556590297273821, + 4451415492203885059, + ], + [ + 10465807219916311101, + 1213997644391575261, + 17672155373280862521, + 1491206970207330736, + 10977478805896263804, + 13260961975618373124, + 16060889403827043708, + 3223573072465920682, + 17624203443801796697, + 10247205738678800822, + 11100653267668698651, + 14328592975764892571, + ], + [ + 6984072551318461094, + 3416562710010527326, + 12847783919251969270, + 12223185134739244472, + 12073170519625198198, + 6221124633828606855, + 17596623990006806590, + 1153871693574764968, + 2548851681903410721, + 9823373270182377847, + 16708030507924899244, + 9619306826188519218, + ], + [ + 5842685042453818473, + 12400879353954910914, + 647112787845575111, + 4893664959929687347, + 3759391664155971284, + 15871181179823725763, + 3629377713951158273, + 3439101502554162312, + 8325686353010019444, + 10630488935940555500, + 3478529754946055748, + 12681233130980545828, + ], ]; fn rescue(mut xs: [F; W]) -> [F; W] { diff --git a/src/util.rs b/src/util.rs index 9e8e5849..c1e32617 100644 --- a/src/util.rs +++ b/src/util.rs @@ -25,9 +25,7 @@ pub(crate) fn log2_strict(n: usize) -> usize { } pub(crate) fn transpose_poly_values(polys: Vec>) -> Vec> { - let poly_values = polys.into_iter() - .map(|p| p.values) - .collect::>(); + let poly_values = polys.into_iter().map(|p| p.values).collect::>(); transpose(&poly_values) } @@ -67,7 +65,7 @@ pub(crate) fn reverse_index_bits_in_place(arr: &mut Vec) { } } -fn reverse_bits(n: usize, num_bits: usize) -> usize { +pub(crate) fn reverse_bits(n: usize, num_bits: usize) -> usize { let mut result = 0; for i in 0..num_bits { let i_rev = num_bits - i - 1; @@ -93,9 +91,11 @@ mod tests { fn test_reverse_index_bits() { assert_eq!( reverse_index_bits(vec![10, 20, 30, 40]), - vec![10, 30, 20, 40]); + vec![10, 30, 20, 40] + ); assert_eq!( reverse_index_bits(vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]), - vec![0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15]); + vec![0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15] + ); } } diff --git a/src/witness.rs b/src/witness.rs index 6f990aea..4c5e89a8 100644 --- a/src/witness.rs +++ b/src/witness.rs @@ -32,12 +32,10 @@ impl PartialWitness { pub fn get_target(&self, target: Target) -> F { self.target_values[&target] -} + } pub fn get_targets(&self, targets: &[Target]) -> Vec { - targets.iter() - .map(|&t| self.get_target(t)) - .collect() + targets.iter().map(|&t| self.get_target(t)).collect() } pub fn try_get_target(&self, target: Target) -> Option { @@ -63,8 +61,11 @@ impl PartialWitness { pub fn set_target(&mut self, target: Target, value: F) { let opt_old_value = self.target_values.insert(target, value); if let Some(old_value) = opt_old_value { - assert_eq!(old_value, value, - "Target was set twice with different values: {:?}", target); + assert_eq!( + old_value, value, + "Target was set twice with different values: {:?}", + target + ); } } From 4d31f5e37bc30a2a96eb1d66267a83f1edb7fae6 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Thu, 22 Apr 2021 09:27:59 +0200 Subject: [PATCH 4/6] Fixes from PR comments --- Cargo.toml | 1 + src/bin/bench_field_mul.rs | 6 +- src/bin/bench_field_mul_interleaved.rs | 6 +- src/fri.rs | 86 +++++++++++++++----------- src/gadgets/split_join.rs | 5 +- src/merkle_proofs.rs | 7 ++- src/merkle_tree.rs | 18 ++---- 7 files changed, 75 insertions(+), 54 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f82cc1de..72c827ab 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,6 +19,7 @@ rand = "0.7.3" rand_chacha = "0.2.2" rayon = "1.5.0" unroll = "0.1.5" +anyhow = "1.0.40" [profile.release] opt-level = 3 diff --git a/src/bin/bench_field_mul.rs b/src/bin/bench_field_mul.rs index 2953e4bd..b2063e50 100644 --- a/src/bin/bench_field_mul.rs +++ b/src/bin/bench_field_mul.rs @@ -20,6 +20,8 @@ fn main() { let duration = start.elapsed(); println!("Result: {:?}", state); - println!("Average field mul: {:?}ns", - duration.as_secs_f64() * 1e9 / EXPONENT as f64); + println!( + "Average field mul: {:?}ns", + duration.as_secs_f64() * 1e9 / EXPONENT as f64 + ); } diff --git a/src/bin/bench_field_mul_interleaved.rs b/src/bin/bench_field_mul_interleaved.rs index b79a93a4..d04415f1 100644 --- a/src/bin/bench_field_mul_interleaved.rs +++ b/src/bin/bench_field_mul_interleaved.rs @@ -28,6 +28,8 @@ fn main() { let duration = start.elapsed(); println!("Result: {:?}", state); - println!("Average field mul: {:?}ns", - duration.as_secs_f64() * 1e9 / (WIDTH * EXPONENT) as f64); + println!( + "Average field mul: {:?}ns", + duration.as_secs_f64() * 1e9 / (WIDTH * EXPONENT) as f64 + ); } diff --git a/src/fri.rs b/src/fri.rs index a67bc93a..90cceef6 100644 --- a/src/fri.rs +++ b/src/fri.rs @@ -7,7 +7,7 @@ use crate::plonk_challenger::Challenger; use crate::polynomial::polynomial::{PolynomialCoeffs, PolynomialValues}; use crate::proof::{FriEvaluations, FriMerkleProofs, FriProof, FriQueryRound, Hash}; use crate::util::log2_strict; -use std::cmp::min; +use anyhow::{ensure, Result}; /// Somewhat arbitrary. Smaller values will increase delta, but with diminishing returns, /// while increasing L, potentially requiring more challenge points. @@ -57,7 +57,7 @@ fn fri_l(codeword_len: usize, rate_log: usize, conjecture: bool) -> f64 { /// Builds a FRI proof. fn fri_proof( // Coefficients of the polynomial on which the LDT is performed. - // Only the first `1/rate_bits` coefficients are non-zero. + // Only the first `1/rate` coefficients are non-zero. polynomial_coeffs: &PolynomialCoeffs, // Evaluation of the polynomial on the large domain. polynomial_values: &PolynomialValues, @@ -108,27 +108,27 @@ fn fri_proof( let x = challenger.get_challenge(); let mut domain_size = n; let mut x_index = x.to_canonical_u64() as usize; - for i in 0..config.reduction_count { - let domain_size2 = domain_size >> 1; + for (i, tree) in trees.iter().enumerate() { + let next_domain_size = domain_size >> 1; x_index %= domain_size; - let minus_x_index = (domain_size2 + x_index) % domain_size; + let minus_x_index = (next_domain_size + x_index) % domain_size; if i == 0 { // For the first layer, we need to send the evaluation at `x` and `-x`. - evals.first_layer = (trees[i].get(x_index)[0], trees[i].get(minus_x_index)[0]); + evals.first_layer = (tree.get(x_index)[0], tree.get(minus_x_index)[0]); } else { // For the other layers, we only need to send the `-x`, the one at `x` can be inferred // by the verifier. See the `compute_evaluation` function. - evals.rest.push(trees[i].get(minus_x_index)[0]); + evals.rest.push(tree.get(minus_x_index)[0]); } merkle_proofs .proofs - .push((trees[i].prove(x_index), trees[i].prove(minus_x_index))); + .push((tree.prove(x_index), tree.prove(minus_x_index))); - domain_size = domain_size2; + domain_size = next_domain_size; } query_round_proofs.push(FriQueryRound { - merkle_proofs, evals, + merkle_proofs, }); } @@ -141,7 +141,7 @@ fn fri_proof( } /// Computes P'(x^2) from P_even(x) and P_odd(x), where P' is the FRI reduced polynomial, -/// P_even is the even coefficients polynomial and P_off is the odd coefficients polynomial. +/// P_even is the even coefficients polynomial and P_odd is the odd coefficients polynomial. fn compute_evaluation(x: F, last_e_x: F, last_e_x_minus: F, beta: F) -> F { // P(x) = P_0(x^2) + xP_1(x^2) // P'(x^2) = P_0(x^2) + beta*P_1(x^2) @@ -153,7 +153,7 @@ fn verify_fri_proof( proof: &FriProof, challenger: &mut Challenger, config: &FriConfig, -) -> Option<()> { +) -> Result<()> { // Size of the LDE domain. let n = proof.final_poly.len() << config.reduction_count; @@ -168,8 +168,14 @@ fn verify_fri_proof( challenger.observe_hash(proof.commit_phase_merkle_roots.last().unwrap()); // Check that parameters are coherent. - assert_eq!(config.num_query_rounds, proof.query_round_proofs.len()); - assert!(config.reduction_count > 0); + ensure!( + config.num_query_rounds == proof.query_round_proofs.len(), + "Number of query rounds does not match config." + ); + ensure!( + config.reduction_count > 0, + "Number of reductions should be non-zero." + ); for round in 0..config.num_query_rounds { let round_proof = &proof.query_round_proofs[round]; @@ -181,8 +187,8 @@ fn verify_fri_proof( let mut subgroup_x = F::primitive_root_of_unity(log2_strict(n)).exp_usize(x_index % n); for i in 0..config.reduction_count { x_index %= domain_size; - let domain_size2 = domain_size >> 1; - let minus_x_index = (domain_size2 + x_index) % domain_size; + let next_domain_size = domain_size >> 1; + let minus_x_index = (next_domain_size + x_index) % domain_size; let (e_x, e_x_minus, merkle_proof, merkle_proof_minus) = if i == 0 { let (e_x, e_x_minus) = round_proof.evals.first_layer; let (merkle_proof, merkle_proof_minus) = &round_proof.merkle_proofs.proofs[i]; @@ -196,27 +202,24 @@ fn verify_fri_proof( e_xs.push((e_x, e_x_minus)); (e_x, e_x_minus, merkle_proof, merkle_proof_minus) }; - (verify_merkle_proof( + verify_merkle_proof( vec![e_x], x_index, proof.commit_phase_merkle_roots[i], merkle_proof, true, - ) - .is_some() - && verify_merkle_proof( - vec![e_x_minus], - minus_x_index, - proof.commit_phase_merkle_roots[i], - merkle_proof_minus, - true, - ) - .is_some()) - .then(|| ())?; + )?; + verify_merkle_proof( + vec![e_x_minus], + minus_x_index, + proof.commit_phase_merkle_roots[i], + merkle_proof_minus, + true, + )?; if i > 0 { subgroup_x = subgroup_x.square(); } - domain_size = domain_size2; + domain_size = next_domain_size; } let (last_e_x, last_e_x_minus) = e_xs[config.reduction_count - 1]; let purported_eval = compute_evaluation( @@ -227,9 +230,13 @@ fn verify_fri_proof( ); // Final check of FRI. After all the reduction, we check that the final polynomial is equal // to the one sent by the prover. - (proof.final_poly.eval(subgroup_x.square()) == purported_eval).then(|| ())?; + ensure!( + proof.final_poly.eval(subgroup_x.square()) == purported_eval, + "Final polynomial evaluation is invalid." + ); } - Some(()) + + Ok(()) } #[cfg(test)] @@ -237,8 +244,14 @@ mod tests { use super::*; use crate::field::crandall_field::CrandallField; use crate::field::fft::ifft; + use anyhow::Result; - fn test_fri(degree: usize, rate_bits: usize, reduction_count: usize, num_query_rounds: usize) { + fn test_fri( + degree: usize, + rate_bits: usize, + reduction_count: usize, + num_query_rounds: usize, + ) -> Result<()> { type F = CrandallField; let n = degree; @@ -254,19 +267,22 @@ mod tests { let proof = fri_proof(&ifft(lde.clone()), &lde, &mut challenger, &config); let mut challenger = Challenger::new(); - assert!(verify_fri_proof(&proof, &mut challenger, &config).is_some()); + verify_fri_proof(&proof, &mut challenger, &config)?; + + Ok(()) } #[test] - fn test_fri_multi_params() { + fn test_fri_multi_params() -> Result<()> { for degree_log in 1..6 { for rate_bits in 0..4 { for reduction_count in 1..=(degree_log + rate_bits) { for num_query_round in 0..4 { - test_fri(1 << degree_log, rate_bits, reduction_count, num_query_round); + test_fri(1 << degree_log, rate_bits, reduction_count, num_query_round)?; } } } } + Ok(()) } } diff --git a/src/gadgets/split_join.rs b/src/gadgets/split_join.rs index eb532ff7..7dbb127c 100644 --- a/src/gadgets/split_join.rs +++ b/src/gadgets/split_join.rs @@ -14,7 +14,10 @@ impl CircuitBuilder { /// enforced elsewhere. pub(crate) fn split_le_virtual(&mut self, integer: Target, num_bits: usize) -> Vec { let bit_targets = self.add_virtual_advice_targets(num_bits); - self.add_generator(SplitGenerator { integer, bits: bit_targets.clone() }); + self.add_generator(SplitGenerator { + integer, + bits: bit_targets.clone(), + }); bit_targets } } diff --git a/src/merkle_proofs.rs b/src/merkle_proofs.rs index d9eb2516..9fe268bc 100644 --- a/src/merkle_proofs.rs +++ b/src/merkle_proofs.rs @@ -7,6 +7,7 @@ use crate::proof::{Hash, HashTarget}; use crate::target::Target; use crate::util::reverse_index_bits; use crate::wire::Wire; +use anyhow::{ensure, Result}; #[derive(Clone, Debug)] pub struct MerkleProof { @@ -27,7 +28,7 @@ pub(crate) fn verify_merkle_proof( merkle_root: Hash, proof: &MerkleProof, reverse_bits: bool, -) -> Option<()> { +) -> Result<()> { let index = if reverse_bits { crate::util::reverse_bits(leaf_index, proof.siblings.len()) } else { @@ -42,7 +43,9 @@ pub(crate) fn verify_merkle_proof( compress(current_digest, sibling_digest) } } - (current_digest == merkle_root).then(|| ()) + ensure!(current_digest == merkle_root, "Invalid Merkle proof."); + + Ok(()) } impl CircuitBuilder { diff --git a/src/merkle_tree.rs b/src/merkle_tree.rs index 7ccf952d..2bd6711f 100644 --- a/src/merkle_tree.rs +++ b/src/merkle_tree.rs @@ -84,9 +84,10 @@ mod tests { use crate::field::crandall_field::CrandallField; use crate::merkle_proofs::verify_merkle_proof; use crate::polynomial::division::divide_by_z_h; + use anyhow::Result; #[test] - fn test_merkle_trees() { + fn test_merkle_trees() -> Result<()> { type F = CrandallField; let n = 1 << 10; @@ -97,22 +98,15 @@ mod tests { let tree = MerkleTree::new(leaves.clone(), false); for i in 0..n { let proof = tree.prove(i); - assert!( - verify_merkle_proof(tree.leaves[i].clone(), i, tree.root, &proof, false).is_some() - ); + verify_merkle_proof(tree.leaves[i].clone(), i, tree.root, &proof, false)?; } let tree_reversed_bits = MerkleTree::new(leaves.clone(), true); for i in 0..n { let proof = tree_reversed_bits.prove(i); - assert!(verify_merkle_proof( - leaves[i].clone(), - i, - tree_reversed_bits.root, - &proof, - true - ) - .is_some()); + verify_merkle_proof(leaves[i].clone(), i, tree_reversed_bits.root, &proof, true)?; } + + Ok(()) } } From a1023e0ca68af41405af04a170f5a742bb2cb63f Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Thu, 22 Apr 2021 15:50:08 +0200 Subject: [PATCH 5/6] Added PoW --- src/fri.rs | 78 +++++++++++++++++++++++++++++++++++--------- src/merkle_proofs.rs | 1 - src/merkle_tree.rs | 12 +++---- src/proof.rs | 2 ++ 4 files changed, 70 insertions(+), 23 deletions(-) diff --git a/src/fri.rs b/src/fri.rs index 90cceef6..01813749 100644 --- a/src/fri.rs +++ b/src/fri.rs @@ -1,11 +1,11 @@ use crate::field::fft::fft; use crate::field::field::Field; -use crate::hash::{compress, hash_n_to_hash}; +use crate::hash::hash_n_to_1; use crate::merkle_proofs::verify_merkle_proof; use crate::merkle_tree::MerkleTree; use crate::plonk_challenger::Challenger; use crate::polynomial::polynomial::{PolynomialCoeffs, PolynomialValues}; -use crate::proof::{FriEvaluations, FriMerkleProofs, FriProof, FriQueryRound, Hash}; +use crate::proof::{FriEvaluations, FriMerkleProofs, FriProof, FriQueryRound}; use crate::util::log2_strict; use anyhow::{ensure, Result}; @@ -14,7 +14,7 @@ use anyhow::{ensure, Result}; const EPSILON: f64 = 0.01; struct FriConfig { - proof_of_work_bits: usize, + proof_of_work_bits: u32, /// The arity of each FRI reduction step, expressed (i.e. the log2 of the actual arity). /// For example, `[3, 2, 1]` would describe a FRI reduction tree with 8-to-1 reduction, then @@ -67,17 +67,41 @@ fn fri_proof( let n = polynomial_values.values.len(); assert_eq!(polynomial_coeffs.coeffs.len(), n); + // Commit phase + let (trees, final_coeffs) = + fri_committed_trees(polynomial_coeffs, polynomial_values, challenger, config); + + let current_hash = challenger.get_challenge(); + let pow_witness = fri_proof_of_work(current_hash, config); + + // Query phase + let query_round_proofs = fri_query_rounds(&trees, challenger, n, config); + + FriProof { + commit_phase_merkle_roots: trees.iter().map(|t| t.root).collect(), + // TODO: Fix this + initial_merkle_proofs: vec![], + query_round_proofs, + final_poly: final_coeffs, + pow_witness, + } +} + +fn fri_committed_trees( + polynomial_coeffs: &PolynomialCoeffs, + polynomial_values: &PolynomialValues, + challenger: &mut Challenger, + config: &FriConfig, +) -> (Vec>, PolynomialCoeffs) { let mut trees = vec![MerkleTree::new( polynomial_values.values.iter().map(|&v| vec![v]).collect(), true, )]; - let mut root = trees[0].root; let mut coeffs = polynomial_coeffs.clone(); let mut values; - challenger.observe_hash(&root); + challenger.observe_hash(&trees[0].root); - // Commit phase for _ in 0..config.reduction_count { let beta = challenger.get_challenge(); // P(x) = P_0(x^2) + xP_1(x^2) becomes P_0(x) + beta*P_1(x) @@ -94,8 +118,27 @@ fn fri_proof( challenger.observe_hash(&tree.root); trees.push(tree); } + (trees, coeffs) +} - // Query phase +fn fri_proof_of_work(current_hash: F, config: &FriConfig) -> F { + (0u64..) + .find(|&i| { + hash_n_to_1(vec![current_hash, F::from_canonical_u64(i)], false) + .to_canonical_u64() + .leading_zeros() + >= config.proof_of_work_bits + }) + .map(F::from_canonical_u64) + .expect("Proof of work failed.") +} + +fn fri_query_rounds( + trees: &[MerkleTree], + challenger: &mut Challenger, + n: usize, + config: &FriConfig, +) -> Vec> { let mut query_round_proofs = Vec::new(); for _ in 0..config.num_query_rounds { let mut merkle_proofs = FriMerkleProofs { proofs: Vec::new() }; @@ -131,13 +174,7 @@ fn fri_proof( merkle_proofs, }); } - - FriProof { - commit_phase_merkle_roots: trees.iter().map(|t| t.root).collect(), - initial_merkle_proofs: vec![], - query_round_proofs, - final_poly: coeffs, - } + query_round_proofs } /// Computes P'(x^2) from P_even(x) and P_odd(x), where P' is the FRI reduced polynomial, @@ -167,6 +204,15 @@ fn verify_fri_proof( .collect::>(); challenger.observe_hash(proof.commit_phase_merkle_roots.last().unwrap()); + // Check PoW. + ensure!( + hash_n_to_1(vec![challenger.get_challenge(), proof.pow_witness], false) + .to_canonical_u64() + .leading_zeros() + >= config.proof_of_work_bits, + "Invalid proof of work witness." + ); + // Check that parameters are coherent. ensure!( config.num_query_rounds == proof.query_round_proofs.len(), @@ -228,7 +274,7 @@ fn verify_fri_proof( last_e_x_minus, betas[config.reduction_count - 1], ); - // Final check of FRI. After all the reduction, we check that the final polynomial is equal + // Final check of FRI. After all the reductions, we check that the final polynomial is equal // to the one sent by the prover. ensure!( proof.final_poly.eval(subgroup_x.square()) == purported_eval, @@ -260,7 +306,7 @@ mod tests { let config = FriConfig { reduction_count, num_query_rounds, - proof_of_work_bits: 0, + proof_of_work_bits: 2, reduction_arity_bits: Vec::new(), }; let mut challenger = Challenger::new(); diff --git a/src/merkle_proofs.rs b/src/merkle_proofs.rs index 9fe268bc..edcdee42 100644 --- a/src/merkle_proofs.rs +++ b/src/merkle_proofs.rs @@ -5,7 +5,6 @@ use crate::hash::GMIMC_ROUNDS; use crate::hash::{compress, hash_or_noop}; use crate::proof::{Hash, HashTarget}; use crate::target::Target; -use crate::util::reverse_index_bits; use crate::wire::Wire; use anyhow::{ensure, Result}; diff --git a/src/merkle_tree.rs b/src/merkle_tree.rs index 2bd6711f..f9a6d8f3 100644 --- a/src/merkle_tree.rs +++ b/src/merkle_tree.rs @@ -1,5 +1,5 @@ use crate::field::field::Field; -use crate::hash::{compress, hash_n_to_hash, hash_or_noop}; +use crate::hash::{compress, hash_or_noop}; use crate::merkle_proofs::MerkleProof; use crate::proof::Hash; use crate::util::{log2_strict, reverse_bits, reverse_index_bits_in_place}; @@ -33,11 +33,11 @@ impl MerkleTree { if l.len() == 1 { break; } - layers.push( - l.chunks(2) - .map(|chunk| compress(chunk[0], chunk[1])) - .collect::>(), - ); + let next_layer = l + .chunks(2) + .map(|chunk| compress(chunk[0], chunk[1])) + .collect::>(); + layers.push(next_layer); } let root = layers.pop().unwrap()[0]; Self { diff --git a/src/proof.rs b/src/proof.rs index 35a64527..9e41bcd9 100644 --- a/src/proof.rs +++ b/src/proof.rs @@ -110,6 +110,8 @@ pub struct FriProof { pub query_round_proofs: Vec>, /// The final polynomial in coefficient form. pub final_poly: PolynomialCoeffs, + /// Witness showing that the prover did PoW. + pub pow_witness: F, } /// Represents a single FRI query, i.e. a path through the reduction tree. From d5433ffe24a15be906b0f841dbe99a8a627b80ab Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Thu, 22 Apr 2021 22:21:24 +0200 Subject: [PATCH 6/6] PoW fixes based on PR comments --- src/fri.rs | 56 ++++++++++++++++++++++++++++++----------- src/plonk_challenger.rs | 11 ++++++++ 2 files changed, 53 insertions(+), 14 deletions(-) diff --git a/src/fri.rs b/src/fri.rs index 01813749..13885cc9 100644 --- a/src/fri.rs +++ b/src/fri.rs @@ -5,9 +5,10 @@ use crate::merkle_proofs::verify_merkle_proof; use crate::merkle_tree::MerkleTree; use crate::plonk_challenger::Challenger; use crate::polynomial::polynomial::{PolynomialCoeffs, PolynomialValues}; -use crate::proof::{FriEvaluations, FriMerkleProofs, FriProof, FriQueryRound}; +use crate::proof::{FriEvaluations, FriMerkleProofs, FriProof, FriQueryRound, Hash}; use crate::util::log2_strict; use anyhow::{ensure, Result}; +use std::iter::FromIterator; /// Somewhat arbitrary. Smaller values will increase delta, but with diminishing returns, /// while increasing L, potentially requiring more challenge points. @@ -71,7 +72,7 @@ fn fri_proof( let (trees, final_coeffs) = fri_committed_trees(polynomial_coeffs, polynomial_values, challenger, config); - let current_hash = challenger.get_challenge(); + let current_hash = challenger.get_hash(); let pow_witness = fri_proof_of_work(current_hash, config); // Query phase @@ -121,18 +122,52 @@ fn fri_committed_trees( (trees, coeffs) } -fn fri_proof_of_work(current_hash: F, config: &FriConfig) -> F { +fn fri_proof_of_work(current_hash: Hash, config: &FriConfig) -> F { (0u64..) .find(|&i| { - hash_n_to_1(vec![current_hash, F::from_canonical_u64(i)], false) - .to_canonical_u64() - .leading_zeros() + hash_n_to_1( + Vec::from_iter( + current_hash + .elements + .iter() + .copied() + .chain(Some(F::from_canonical_u64(i))), + ), + false, + ) + .to_canonical_u64() + .leading_zeros() >= config.proof_of_work_bits }) .map(F::from_canonical_u64) .expect("Proof of work failed.") } +fn fri_verify_proof_of_work( + proof: &FriProof, + challenger: &mut Challenger, + config: &FriConfig, +) -> Result<()> { + let hash = hash_n_to_1( + Vec::from_iter( + challenger + .get_hash() + .elements + .iter() + .copied() + .chain(Some(proof.pow_witness)), + ), + false, + ); + ensure!( + hash.to_canonical_u64().leading_zeros() + >= config.proof_of_work_bits + F::ORDER.leading_zeros(), + "Invalid proof of work witness." + ); + + Ok(()) +} + fn fri_query_rounds( trees: &[MerkleTree], challenger: &mut Challenger, @@ -205,14 +240,7 @@ fn verify_fri_proof( challenger.observe_hash(proof.commit_phase_merkle_roots.last().unwrap()); // Check PoW. - ensure!( - hash_n_to_1(vec![challenger.get_challenge(), proof.pow_witness], false) - .to_canonical_u64() - .leading_zeros() - >= config.proof_of_work_bits, - "Invalid proof of work witness." - ); - + fri_verify_proof_of_work(proof, challenger, config)?; // Check that parameters are coherent. ensure!( config.num_query_rounds == proof.query_round_proofs.len(), diff --git a/src/plonk_challenger.rs b/src/plonk_challenger.rs index 0008278f..a98a6fc3 100644 --- a/src/plonk_challenger.rs +++ b/src/plonk_challenger.rs @@ -76,6 +76,17 @@ impl Challenger { (0..n).map(|_| self.get_challenge()).collect() } + pub fn get_hash(&mut self) -> Hash { + Hash { + elements: [ + self.get_challenge(), + self.get_challenge(), + self.get_challenge(), + self.get_challenge(), + ], + } + } + /// Absorb any buffered inputs. After calling this, the input buffer will be empty. fn absorb_buffered_inputs(&mut self) { for input_chunk in self.input_buffer.chunks(SPONGE_RATE) {