diff --git a/Cargo.toml b/Cargo.toml index 21708d41..8c4968ec 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,9 +18,11 @@ itertools = "0.10.0" num = "0.3" rand = "0.7.3" rand_chacha = "0.2.2" -rayon = "1.5.0" +rayon = "1.5.1" unroll = "0.1.5" anyhow = "1.0.40" +serde = { version = "1.0", features = ["derive"] } +serde_cbor = "0.11.1" [profile.release] opt-level = 3 diff --git a/src/bin/bench_gmimc.rs b/src/bin/bench_gmimc.rs index 2f81aac0..9611d48e 100644 --- a/src/bin/bench_gmimc.rs +++ b/src/bin/bench_gmimc.rs @@ -1,3 +1,4 @@ +use std::convert::TryInto; use std::thread; use std::time::Instant; @@ -15,15 +16,12 @@ fn main() { const THREADS: usize = 12; const LDE_BITS: i32 = 3; const W: usize = 12; - const HASHES_PER_POLY: usize = 1 << (13 + LDE_BITS) / 6; + const HASHES_PER_POLY: usize = 1 << ((13 + LDE_BITS) / 6); 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 mut x: [F; W] = F::rand_vec(W).try_into().unwrap(); let hashes_per_thread = HASHES_PER_POLY * PROVER_POLYS / THREADS; let start = Instant::now(); diff --git a/src/bin/bench_recursion.rs b/src/bin/bench_recursion.rs index b75d4bdc..51f93025 100644 --- a/src/bin/bench_recursion.rs +++ b/src/bin/bench_recursion.rs @@ -1,4 +1,5 @@ use env_logger::Env; +use log::info; use plonky2::circuit_builder::CircuitBuilder; use plonky2::circuit_data::CircuitConfig; use plonky2::field::crandall_field::CrandallField; @@ -25,10 +26,10 @@ fn bench_prove, const D: usize>() { rate_bits: 3, num_challenges: 3, fri_config: FriConfig { - proof_of_work_bits: 1, + proof_of_work_bits: 20, rate_bits: 3, - reduction_arity_bits: vec![2, 2, 2, 2, 2], - num_query_rounds: 1, + reduction_arity_bits: vec![2, 2, 2, 2, 2, 2], + num_query_rounds: 35, }, }; @@ -46,7 +47,10 @@ fn bench_prove, const D: usize>() { builder.add(zero, zero); builder.add_extension(zero_ext, zero_ext); - let prover = builder.build_prover(); + let circuit = builder.build(); let inputs = PartialWitness::new(); - prover.prove(inputs); + let proof = circuit.prove(inputs); + let proof_bytes = serde_cbor::to_vec(&proof).unwrap(); + info!("Proof length: {} bytes", proof_bytes.len()); + circuit.verify(proof).unwrap(); } diff --git a/src/bin/bench_rescue.rs b/src/bin/bench_rescue.rs index 96334689..1b45c31c 100644 --- a/src/bin/bench_rescue.rs +++ b/src/bin/bench_rescue.rs @@ -1,3 +1,4 @@ +use std::convert::TryInto; use std::thread; use std::time::Instant; @@ -19,10 +20,7 @@ fn main() { 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 mut x: [F; W] = F::rand_vec(W).try_into().unwrap(); let hashes_per_thread = HASHES_PER_POLY * PROVER_POLYS / THREADS; let start = Instant::now(); diff --git a/src/circuit_data.rs b/src/circuit_data.rs index e389ed27..ac3ede94 100644 --- a/src/circuit_data.rs +++ b/src/circuit_data.rs @@ -39,7 +39,7 @@ impl Default for CircuitConfig { num_challenges: 3, fri_config: FriConfig { proof_of_work_bits: 1, - rate_bits: 1, + rate_bits: 3, reduction_arity_bits: vec![1, 1, 1, 1], num_query_rounds: 1, }, diff --git a/src/field/crandall_field.rs b/src/field/crandall_field.rs index 7a1d18d6..9d38ff1a 100644 --- a/src/field/crandall_field.rs +++ b/src/field/crandall_field.rs @@ -5,6 +5,7 @@ use std::iter::{Product, Sum}; use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign}; use num::Integer; +use serde::{Deserialize, Serialize}; use crate::field::extension_field::quadratic::QuadraticCrandallField; use crate::field::extension_field::quartic::QuarticCrandallField; @@ -106,7 +107,7 @@ const CAUCHY_MDS_8: [[CrandallField; 8]; 8] = [ /// = 2**64 - 9 * 2**28 + 1 /// = 2**28 * (2**36 - 9) + 1 /// ``` -#[derive(Copy, Clone)] +#[derive(Copy, Clone, Serialize, Deserialize)] pub struct CrandallField(pub u64); impl PartialEq for CrandallField { diff --git a/src/field/extension_field/quadratic.rs b/src/field/extension_field/quadratic.rs index ede2ef26..256803ab 100644 --- a/src/field/extension_field/quadratic.rs +++ b/src/field/extension_field/quadratic.rs @@ -4,12 +4,13 @@ use std::iter::{Product, Sum}; use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign}; use rand::Rng; +use serde::{Deserialize, Serialize}; use crate::field::crandall_field::CrandallField; use crate::field::extension_field::{FieldExtension, Frobenius, OEF}; use crate::field::field::Field; -#[derive(Copy, Clone, Eq, PartialEq, Hash)] +#[derive(Copy, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)] pub struct QuadraticCrandallField([CrandallField; 2]); impl OEF<2> for QuadraticCrandallField { diff --git a/src/field/extension_field/quartic.rs b/src/field/extension_field/quartic.rs index f609eeb7..22e91353 100644 --- a/src/field/extension_field/quartic.rs +++ b/src/field/extension_field/quartic.rs @@ -4,13 +4,14 @@ use std::iter::{Product, Sum}; use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign}; use rand::Rng; +use serde::{Deserialize, Serialize}; use crate::field::crandall_field::CrandallField; use crate::field::extension_field::{FieldExtension, Frobenius, OEF}; use crate::field::field::Field; /// A quartic extension of `CrandallField`. -#[derive(Copy, Clone, Eq, PartialEq, Hash)] +#[derive(Copy, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)] pub struct QuarticCrandallField(pub(crate) [CrandallField; 4]); impl OEF<4> for QuarticCrandallField { diff --git a/src/field/fft.rs b/src/field/fft.rs index fa65a5ea..35b11852 100644 --- a/src/field/fft.rs +++ b/src/field/fft.rs @@ -250,9 +250,9 @@ fn fft_unrolled(input: Vec, r_orig: usize, root_table: FftRootTable // NB: Grouping statements as is done in the main loop below // does not seem to help here (worse by a few millis). let omega_0 = root_table[0][0]; - let tmp_0 = omega_0 * values[k + 2 + 0]; - values[k + 2 + 0] = values[k + 0] - tmp_0; - values[k + 0] += tmp_0; + let tmp_0 = omega_0 * values[k + 2]; + values[k + 2] = values[k] - tmp_0; + values[k] += tmp_0; let omega_1 = root_table[0][1]; let tmp_1 = omega_1 * values[k + 2 + 1]; @@ -281,21 +281,21 @@ fn fft_unrolled(input: Vec, r_orig: usize, root_table: FftRootTable let off1 = k + j; let off2 = k + m + j; - let omega_0 = root_table[lg_m - 1][j + 0]; + let omega_0 = root_table[lg_m - 1][j]; let omega_1 = root_table[lg_m - 1][j + 1]; let omega_2 = root_table[lg_m - 1][j + 2]; let omega_3 = root_table[lg_m - 1][j + 3]; - let tmp_0 = omega_0 * values[off2 + 0]; + let tmp_0 = omega_0 * values[off2]; let tmp_1 = omega_1 * values[off2 + 1]; let tmp_2 = omega_2 * values[off2 + 2]; let tmp_3 = omega_3 * values[off2 + 3]; - values[off2 + 0] = values[off1 + 0] - tmp_0; + values[off2] = values[off1] - tmp_0; values[off2 + 1] = values[off1 + 1] - tmp_1; values[off2 + 2] = values[off1 + 2] - tmp_2; values[off2 + 3] = values[off1 + 3] - tmp_3; - values[off1 + 0] += tmp_0; + values[off1] += tmp_0; values[off1 + 1] += tmp_1; values[off1 + 2] += tmp_2; values[off1 + 3] += tmp_3; diff --git a/src/field/field.rs b/src/field/field.rs index f4ef5990..28a52202 100644 --- a/src/field/field.rs +++ b/src/field/field.rs @@ -6,6 +6,8 @@ use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssi use num::Integer; use rand::Rng; +use serde::de::DeserializeOwned; +use serde::Serialize; use crate::field::extension_field::Frobenius; use crate::util::bits_u64; @@ -31,6 +33,8 @@ pub trait Field: + Display + Send + Sync + + Serialize + + DeserializeOwned { type PrimeField: Field; @@ -131,7 +135,7 @@ pub trait Field: fn primitive_root_of_unity(n_log: usize) -> Self { assert!(n_log <= Self::TWO_ADICITY); - let mut base = Self::POWER_OF_TWO_GENERATOR; + let base = Self::POWER_OF_TWO_GENERATOR; base.exp_power_of_2(Self::TWO_ADICITY - n_log) } diff --git a/src/fri/prover.rs b/src/fri/prover.rs index f937ee55..12968c06 100644 --- a/src/fri/prover.rs +++ b/src/fri/prover.rs @@ -1,3 +1,5 @@ +use rayon::prelude::*; + use crate::field::extension_field::{flatten, unflatten, Extendable}; use crate::field::field::Field; use crate::fri::FriConfig; @@ -7,6 +9,7 @@ use crate::plonk_challenger::Challenger; use crate::plonk_common::reduce_with_powers; use crate::polynomial::polynomial::{PolynomialCoeffs, PolynomialValues}; use crate::proof::{FriInitialTreeProof, FriProof, FriQueryRound, FriQueryStep, Hash}; +use crate::timed; use crate::util::reverse_index_bits_in_place; /// Builds a FRI proof. @@ -32,7 +35,10 @@ pub fn fri_proof, const D: usize>( // PoW phase let current_hash = challenger.get_hash(); - let pow_witness = fri_proof_of_work(current_hash, config); + let pow_witness = timed!( + fri_proof_of_work(current_hash, config), + "to find for proof-of-work witness" + ); // Query phase let query_round_proofs = @@ -94,8 +100,9 @@ fn fri_committed_trees, const D: usize>( } fn fri_proof_of_work(current_hash: Hash, config: &FriConfig) -> F { - (0u64..) - .find(|&i| { + (0..u64::MAX) + .into_par_iter() + .find_any(|&i| { hash_n_to_1( current_hash .elements @@ -110,7 +117,7 @@ fn fri_proof_of_work(current_hash: Hash, config: &FriConfig) -> F { >= config.proof_of_work_bits + F::ORDER.leading_zeros() }) .map(F::from_canonical_u64) - .expect("Proof of work failed.") + .expect("Proof of work failed. This is highly unlikely!") } fn fri_prover_query_rounds, const D: usize>( diff --git a/src/gates/mod.rs b/src/gates/mod.rs index fa23b273..298c9138 100644 --- a/src/gates/mod.rs +++ b/src/gates/mod.rs @@ -1,3 +1,6 @@ +// Gates have `new` methods that return `GateRef`s. +#![allow(clippy::new_ret_no_self)] + pub mod arithmetic; pub mod base_sum; pub mod constant; diff --git a/src/gmimc.rs b/src/gmimc.rs index 9a65d49d..db80f19a 100644 --- a/src/gmimc.rs +++ b/src/gmimc.rs @@ -56,8 +56,8 @@ pub fn gmimc_permute_array( xs[active] -= f; } - for i in 0..W { - xs[i] += addition_buffer; + for x_i in xs.iter_mut() { + *x_i += addition_buffer; } xs diff --git a/src/hash.rs b/src/hash.rs index fd551a6c..2c7bad64 100644 --- a/src/hash.rs +++ b/src/hash.rs @@ -117,12 +117,6 @@ pub const GMIMC_CONSTANTS: [u64; GMIMC_ROUNDS] = [ 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 -/// elements. If this is too small, there may be too much synchronization overhead; if it's too -/// large, some threads may spend significant time idle. -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(inputs: Vec) -> Hash { @@ -226,8 +220,8 @@ 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_RATE { - outputs.push(state[i]); + for &item in state.iter().take(SPONGE_RATE) { + outputs.push(item); if outputs.len() == num_outputs { return outputs; } diff --git a/src/merkle_proofs.rs b/src/merkle_proofs.rs index a9aeeb27..b7625f63 100644 --- a/src/merkle_proofs.rs +++ b/src/merkle_proofs.rs @@ -1,4 +1,5 @@ use anyhow::{ensure, Result}; +use serde::{Deserialize, Serialize}; use crate::circuit_builder::CircuitBuilder; use crate::field::extension_field::Extendable; @@ -10,7 +11,8 @@ use crate::proof::{Hash, HashTarget}; use crate::target::Target; use crate::wire::Wire; -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(bound = "")] pub struct MerkleProof { /// The Merkle digest of each sibling subtree, staying from the bottommost layer. pub siblings: Vec>, diff --git a/src/permutation_argument.rs b/src/permutation_argument.rs index 5c1ead6a..9a692cd3 100644 --- a/src/permutation_argument.rs +++ b/src/permutation_argument.rs @@ -46,7 +46,7 @@ impl usize> TargetPartition } /// Path compression method, see https://en.wikipedia.org/wiki/Disjoint-set_data_structure#Finding_set_representatives. - pub fn find(&mut self, mut x: ForestNode) -> ForestNode { + pub fn find(&mut self, x: ForestNode) -> ForestNode { if x.parent != x.index { let root = self.find(self.forest[x.parent]); self.forest[x.index].parent = root.index; diff --git a/src/polynomial/commitment.rs b/src/polynomial/commitment.rs index 7c580ad9..4436a454 100644 --- a/src/polynomial/commitment.rs +++ b/src/polynomial/commitment.rs @@ -1,5 +1,6 @@ use anyhow::Result; use rayon::prelude::*; +use serde::{Deserialize, Serialize}; use crate::circuit_builder::CircuitBuilder; use crate::circuit_data::CommonCircuitData; @@ -247,9 +248,10 @@ impl ListPolynomialCommitment { } } -#[derive(Clone)] +#[derive(Serialize, Deserialize, Debug)] +#[serde(bound = "")] pub struct OpeningProof, const D: usize> { - pub fri_proof: FriProof, + fri_proof: FriProof, // TODO: Get the degree from `CommonCircuitData` instead. quotient_degree: usize, } @@ -281,7 +283,7 @@ impl, const D: usize> OpeningProof { } pub struct OpeningProofTarget { - pub fri_proof: FriProofTarget, + fri_proof: FriProofTarget, } impl OpeningProofTarget { diff --git a/src/polynomial/polynomial.rs b/src/polynomial/polynomial.rs index 81d07b8f..5f9bccd9 100644 --- a/src/polynomial/polynomial.rs +++ b/src/polynomial/polynomial.rs @@ -3,6 +3,7 @@ use std::iter::Sum; use std::ops::{Add, AddAssign, Mul, MulAssign, Sub, SubAssign}; use anyhow::{ensure, Result}; +use serde::{Deserialize, Serialize}; use crate::field::extension_field::Extendable; use crate::field::fft::{fft, fft_with_options, ifft}; @@ -76,7 +77,8 @@ impl From> for PolynomialValues { } /// A polynomial in coefficient form. -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(bound = "")] pub struct PolynomialCoeffs { pub(crate) coeffs: Vec, } diff --git a/src/proof.rs b/src/proof.rs index b5875927..85ef44b5 100644 --- a/src/proof.rs +++ b/src/proof.rs @@ -1,5 +1,7 @@ use std::convert::TryInto; +use serde::{Deserialize, Serialize}; + use crate::circuit_data::CommonCircuitData; use crate::field::extension_field::target::ExtensionTarget; use crate::field::extension_field::Extendable; @@ -12,7 +14,8 @@ use crate::polynomial::polynomial::PolynomialCoeffs; use crate::target::Target; /// Represents a ~256 bit hash output. -#[derive(Copy, Clone, Debug, Eq, PartialEq)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] +#[serde(bound = "")] pub struct Hash { pub(crate) elements: [F; 4], } @@ -61,7 +64,8 @@ impl HashTarget { } } -#[derive(Clone)] +#[derive(Serialize, Deserialize, Clone, Debug)] +#[serde(bound = "")] pub struct Proof, const D: usize> { /// Merkle root of LDEs of wire values. pub wires_root: Hash, @@ -84,8 +88,9 @@ pub struct ProofTarget { } /// Evaluations and Merkle proof produced by the prover in a FRI query step. -#[derive(Clone)] -pub struct FriQueryStep, const D: usize> { +#[derive(Serialize, Deserialize, Clone, Debug)] +#[serde(bound = "")] +pub struct FriQueryStep, const D: usize> { pub evals: Vec, pub merkle_proof: MerkleProof, } @@ -98,7 +103,8 @@ pub struct FriQueryStepTarget { /// Evaluations and Merkle proofs of the original set of polynomials, /// before they are combined into a composition polynomial. -#[derive(Clone)] +#[derive(Serialize, Deserialize, Clone, Debug)] +#[serde(bound = "")] pub struct FriInitialTreeProof { pub evals_proofs: Vec<(Vec, MerkleProof)>, } @@ -123,8 +129,9 @@ impl FriInitialTreeProofTarget { } /// Proof for a FRI query round. -#[derive(Clone)] -pub struct FriQueryRound, const D: usize> { +#[derive(Serialize, Deserialize, Clone, Debug)] +#[serde(bound = "")] +pub struct FriQueryRound, const D: usize> { pub initial_trees_proof: FriInitialTreeProof, pub steps: Vec>, } @@ -135,8 +142,9 @@ pub struct FriQueryRoundTarget { pub steps: Vec>, } -#[derive(Clone)] -pub struct FriProof, const D: usize> { +#[derive(Serialize, Deserialize, Clone, Debug)] +#[serde(bound = "")] +pub struct FriProof, const D: usize> { /// A Merkle root for each reduced polynomial in the commit phase. pub commit_phase_merkle_roots: Vec>, /// Query rounds proofs @@ -154,9 +162,9 @@ pub struct FriProofTarget { pub pow_witness: Target, } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Serialize, Deserialize)] /// The purported values of each polynomial at a single point. -pub struct OpeningSet, const D: usize> { +pub struct OpeningSet, const D: usize> { pub constants: Vec, pub plonk_sigmas: Vec, pub wires: Vec, @@ -166,7 +174,7 @@ pub struct OpeningSet, const D: usize> { pub quotient_polys: Vec, } -impl, const D: usize> OpeningSet { +impl, const D: usize> OpeningSet { pub fn new( z: F::Extension, g: F::Extension, diff --git a/src/prover.rs b/src/prover.rs index 0411ce09..ce83f0a5 100644 --- a/src/prover.rs +++ b/src/prover.rs @@ -86,7 +86,7 @@ pub(crate) fn prove, const D: usize>( let gammas = challenger.get_n_challenges(num_challenges); assert!( - common_data.quotient_degree_factor + 1 <=common_data.config.num_routed_wires, + common_data.quotient_degree_factor < common_data.config.num_routed_wires, "When the number of routed wires is smaller that the degree, we should change the logic to avoid computing partial products." ); let mut partial_products = timed!( diff --git a/src/rescue.rs b/src/rescue.rs index f088fdb7..9aa12bc5 100644 --- a/src/rescue.rs +++ b/src/rescue.rs @@ -179,7 +179,7 @@ const MDS: [[u64; W]; W] = [ ], ]; -const RESCUE_CONSTANTS: [[u64; W]; 16] = [ +const RESCUE_CONSTANTS: [[u64; W]; ROUNDS * 2] = [ [ 12050887499329086906, 1748247961703512657, @@ -442,7 +442,7 @@ fn mds_layer(x: [F; W]) -> [F; W] { let mut result = [F::ZERO; W]; for r in 0..W { for c in 0..W { - result[r] = result[r] + F::from_canonical_u64(MDS[r][c]) * x[c]; + result[r] += F::from_canonical_u64(MDS[r][c]) * x[c]; } } result diff --git a/src/util/mod.rs b/src/util/mod.rs index 6fd0e562..bfebe058 100644 --- a/src/util/mod.rs +++ b/src/util/mod.rs @@ -43,8 +43,8 @@ pub(crate) fn transpose(matrix: &[Vec]) -> Vec> { let old_cols = matrix[0].len(); let mut transposed = vec![Vec::with_capacity(old_rows); old_cols]; for new_r in 0..old_cols { - for new_c in 0..old_rows { - transposed[new_r].push(matrix[new_c][new_r].clone()); + for old_row in matrix.iter() { + transposed[new_r].push(old_row[new_r].clone()); } } transposed