2021-05-14 07:33:46 -07:00
|
|
|
use rayon::prelude::*;
|
|
|
|
|
|
2021-06-14 10:33:38 +02:00
|
|
|
use crate::field::extension_field::Extendable;
|
2021-09-22 10:56:09 -07:00
|
|
|
use crate::field::fft::FftRootTable;
|
2021-09-07 18:28:28 -07:00
|
|
|
use crate::field::field_types::{Field, RichField};
|
2021-08-08 09:14:07 -07:00
|
|
|
use crate::fri::proof::FriProof;
|
2021-08-10 16:18:42 +02:00
|
|
|
use crate::fri::prover::fri_proof;
|
2021-07-29 22:00:29 -07:00
|
|
|
use crate::hash::merkle_tree::MerkleTree;
|
2021-08-08 09:14:07 -07:00
|
|
|
use crate::iop::challenger::Challenger;
|
2021-07-29 22:00:29 -07:00
|
|
|
use crate::plonk::circuit_data::CommonCircuitData;
|
|
|
|
|
use crate::plonk::plonk_common::PlonkPolynomials;
|
2021-08-08 09:14:07 -07:00
|
|
|
use crate::plonk::proof::OpeningSet;
|
2021-06-17 15:49:21 +02:00
|
|
|
use crate::polynomial::polynomial::{PolynomialCoeffs, PolynomialValues};
|
2021-05-14 07:33:46 -07:00
|
|
|
use crate::timed;
|
2021-07-23 17:31:00 +02:00
|
|
|
use crate::util::reducing::ReducingFactor;
|
2021-08-02 10:38:09 -07:00
|
|
|
use crate::util::timing::TimingTree;
|
2021-08-08 09:14:07 -07:00
|
|
|
use crate::util::{log2_strict, reverse_bits, reverse_index_bits_in_place, transpose};
|
2021-04-30 15:07:54 +02:00
|
|
|
|
2021-07-18 23:24:33 -07:00
|
|
|
/// Two (~64 bit) field elements gives ~128 bit security.
|
2021-05-06 17:09:55 +02:00
|
|
|
pub const SALT_SIZE: usize = 2;
|
|
|
|
|
|
2021-07-29 22:00:29 -07:00
|
|
|
/// Represents a batch FRI based commitment to a list of polynomials.
|
2021-09-07 18:28:28 -07:00
|
|
|
pub struct PolynomialBatchCommitment<F: RichField> {
|
2021-05-03 15:17:05 +02:00
|
|
|
pub polynomials: Vec<PolynomialCoeffs<F>>,
|
2021-04-30 15:07:54 +02:00
|
|
|
pub merkle_tree: MerkleTree<F>,
|
2021-06-17 15:49:21 +02:00
|
|
|
pub degree_log: usize,
|
2021-05-11 09:56:21 +02:00
|
|
|
pub rate_bits: usize,
|
|
|
|
|
pub blinding: bool,
|
2021-04-30 15:07:54 +02:00
|
|
|
}
|
|
|
|
|
|
2021-09-07 18:28:28 -07:00
|
|
|
impl<F: RichField> PolynomialBatchCommitment<F> {
|
2021-06-17 15:49:21 +02:00
|
|
|
/// Creates a list polynomial commitment for the polynomials interpolating the values in `values`.
|
2021-08-09 10:11:35 -07:00
|
|
|
pub(crate) fn from_values(
|
2021-08-02 10:38:09 -07:00
|
|
|
values: Vec<PolynomialValues<F>>,
|
|
|
|
|
rate_bits: usize,
|
|
|
|
|
blinding: bool,
|
2021-08-10 13:33:44 +02:00
|
|
|
cap_height: usize,
|
2021-08-02 10:38:09 -07:00
|
|
|
timing: &mut TimingTree,
|
2021-09-22 10:56:09 -07:00
|
|
|
fft_root_table: Option<&FftRootTable<F>>,
|
2021-08-02 10:38:09 -07:00
|
|
|
) -> Self {
|
2021-08-09 10:11:35 -07:00
|
|
|
let coeffs = timed!(
|
2021-08-02 10:38:09 -07:00
|
|
|
timing,
|
2021-08-09 10:11:35 -07:00
|
|
|
"IFFT",
|
|
|
|
|
values.par_iter().map(|v| v.ifft()).collect::<Vec<_>>()
|
2021-06-17 15:49:21 +02:00
|
|
|
);
|
|
|
|
|
|
2021-09-22 10:56:09 -07:00
|
|
|
Self::from_coeffs(
|
|
|
|
|
coeffs,
|
|
|
|
|
rate_bits,
|
|
|
|
|
blinding,
|
|
|
|
|
cap_height,
|
|
|
|
|
timing,
|
|
|
|
|
fft_root_table,
|
|
|
|
|
)
|
2021-06-17 15:49:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Creates a list polynomial commitment for the polynomials `polynomials`.
|
2021-08-09 10:11:35 -07:00
|
|
|
pub(crate) fn from_coeffs(
|
2021-06-17 15:49:21 +02:00
|
|
|
polynomials: Vec<PolynomialCoeffs<F>>,
|
|
|
|
|
rate_bits: usize,
|
|
|
|
|
blinding: bool,
|
2021-08-10 13:33:44 +02:00
|
|
|
cap_height: usize,
|
2021-08-02 10:38:09 -07:00
|
|
|
timing: &mut TimingTree,
|
2021-09-22 10:56:09 -07:00
|
|
|
fft_root_table: Option<&FftRootTable<F>>,
|
2021-06-17 15:49:21 +02:00
|
|
|
) -> Self {
|
2021-05-03 15:17:05 +02:00
|
|
|
let degree = polynomials[0].len();
|
2021-05-14 07:33:46 -07:00
|
|
|
let lde_values = timed!(
|
2021-08-02 10:38:09 -07:00
|
|
|
timing,
|
2021-08-09 10:11:35 -07:00
|
|
|
"FFT + blinding",
|
2021-09-22 10:56:09 -07:00
|
|
|
Self::lde_values(&polynomials, rate_bits, blinding, fft_root_table)
|
2021-05-14 07:33:46 -07:00
|
|
|
);
|
|
|
|
|
|
2021-08-02 10:38:09 -07:00
|
|
|
let mut leaves = timed!(timing, "transpose LDEs", transpose(&lde_values));
|
2021-05-14 07:33:46 -07:00
|
|
|
reverse_index_bits_in_place(&mut leaves);
|
2021-08-10 13:33:44 +02:00
|
|
|
let merkle_tree = timed!(
|
|
|
|
|
timing,
|
|
|
|
|
"build Merkle tree",
|
2021-08-18 14:36:43 +02:00
|
|
|
MerkleTree::new(leaves, cap_height)
|
2021-08-10 13:33:44 +02:00
|
|
|
);
|
2021-05-14 07:33:46 -07:00
|
|
|
|
|
|
|
|
Self {
|
|
|
|
|
polynomials,
|
|
|
|
|
merkle_tree,
|
2021-06-17 15:49:21 +02:00
|
|
|
degree_log: log2_strict(degree),
|
2021-05-14 07:33:46 -07:00
|
|
|
rate_bits,
|
|
|
|
|
blinding,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn lde_values(
|
|
|
|
|
polynomials: &[PolynomialCoeffs<F>],
|
|
|
|
|
rate_bits: usize,
|
|
|
|
|
blinding: bool,
|
2021-09-22 10:56:09 -07:00
|
|
|
fft_root_table: Option<&FftRootTable<F>>,
|
2021-05-14 07:33:46 -07:00
|
|
|
) -> Vec<Vec<F>> {
|
|
|
|
|
let degree = polynomials[0].len();
|
2021-08-09 10:11:35 -07:00
|
|
|
|
|
|
|
|
// If blinding, salt with two random elements to each leaf vector.
|
|
|
|
|
let salt_size = if blinding { SALT_SIZE } else { 0 };
|
|
|
|
|
|
2021-05-14 07:33:46 -07:00
|
|
|
polynomials
|
2021-05-07 16:49:27 +02:00
|
|
|
.par_iter()
|
2021-05-03 15:17:05 +02:00
|
|
|
.map(|p| {
|
2021-08-09 10:11:35 -07:00
|
|
|
assert_eq!(p.len(), degree, "Polynomial degrees inconsistent");
|
|
|
|
|
p.lde(rate_bits)
|
2021-09-22 10:56:09 -07:00
|
|
|
.coset_fft_with_options(F::coset_shift(), Some(rate_bits), fft_root_table)
|
2021-08-09 10:11:35 -07:00
|
|
|
.values
|
2021-05-06 00:00:08 +02:00
|
|
|
})
|
2021-08-09 10:11:35 -07:00
|
|
|
.chain(
|
|
|
|
|
(0..salt_size)
|
|
|
|
|
.into_par_iter()
|
|
|
|
|
.map(|_| F::rand_vec(degree << rate_bits)),
|
|
|
|
|
)
|
2021-05-14 07:33:46 -07:00
|
|
|
.collect()
|
2021-04-30 15:07:54 +02:00
|
|
|
}
|
2021-05-03 15:17:05 +02:00
|
|
|
|
2021-06-24 14:11:47 +02:00
|
|
|
pub fn get_lde_values(&self, index: usize) -> &[F] {
|
|
|
|
|
let index = reverse_bits(index, self.degree_log + self.rate_bits);
|
2021-06-24 11:45:16 +02:00
|
|
|
let slice = &self.merkle_tree.leaves[index];
|
|
|
|
|
&slice[..slice.len() - if self.blinding { SALT_SIZE } else { 0 }]
|
|
|
|
|
}
|
2021-05-07 16:22:13 +02:00
|
|
|
|
2021-06-01 11:17:54 +02:00
|
|
|
/// Takes the commitments to the constants - sigmas - wires - zs - quotient — polynomials,
|
|
|
|
|
/// and an opening point `zeta` and produces a batched opening proof + opening set.
|
2021-08-02 10:38:09 -07:00
|
|
|
pub(crate) fn open_plonk<const D: usize>(
|
2021-06-25 11:24:26 +02:00
|
|
|
commitments: &[&Self; 4],
|
2021-05-31 17:49:04 +02:00
|
|
|
zeta: F::Extension,
|
2021-05-06 17:09:55 +02:00
|
|
|
challenger: &mut Challenger<F>,
|
2021-06-25 11:24:26 +02:00
|
|
|
common_data: &CommonCircuitData<F, D>,
|
2021-08-02 10:38:09 -07:00
|
|
|
timing: &mut TimingTree,
|
2021-08-08 09:14:07 -07:00
|
|
|
) -> (FriProof<F, D>, OpeningSet<F, D>)
|
2021-05-18 15:22:06 +02:00
|
|
|
where
|
2021-09-07 18:28:28 -07:00
|
|
|
F: RichField + Extendable<D>,
|
2021-05-18 15:22:06 +02:00
|
|
|
{
|
2021-07-18 23:24:33 -07:00
|
|
|
let config = &common_data.config;
|
2021-06-01 21:55:05 +02:00
|
|
|
assert!(D > 1, "Not implemented for D=1.");
|
2021-06-17 15:49:21 +02:00
|
|
|
let degree_log = commitments[0].degree_log;
|
2021-05-31 17:49:04 +02:00
|
|
|
let g = F::Extension::primitive_root_of_unity(degree_log);
|
2021-06-09 16:17:56 -07:00
|
|
|
for p in &[zeta, g * zeta] {
|
2021-05-06 17:09:55 +02:00
|
|
|
assert_ne!(
|
2021-09-05 10:27:11 -07:00
|
|
|
p.exp_u64(1 << degree_log as u64),
|
2021-05-18 15:22:06 +02:00
|
|
|
F::Extension::ONE,
|
2021-05-06 17:09:55 +02:00
|
|
|
"Opening point is in the subgroup."
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2021-08-02 15:49:06 -07:00
|
|
|
let os = timed!(
|
|
|
|
|
timing,
|
|
|
|
|
"construct the opening set",
|
|
|
|
|
OpeningSet::new(
|
|
|
|
|
zeta,
|
|
|
|
|
g,
|
|
|
|
|
commitments[0],
|
|
|
|
|
commitments[1],
|
|
|
|
|
commitments[2],
|
|
|
|
|
commitments[3],
|
|
|
|
|
common_data,
|
|
|
|
|
)
|
2021-05-31 17:49:04 +02:00
|
|
|
);
|
|
|
|
|
challenger.observe_opening_set(&os);
|
2021-05-06 17:09:55 +02:00
|
|
|
|
2021-05-18 15:22:06 +02:00
|
|
|
let alpha = challenger.get_extension_challenge();
|
2021-06-17 22:06:53 +02:00
|
|
|
let mut alpha = ReducingFactor::new(alpha);
|
2021-05-06 17:09:55 +02:00
|
|
|
|
2021-05-31 17:49:04 +02:00
|
|
|
// Final low-degree polynomial that goes into FRI.
|
|
|
|
|
let mut final_poly = PolynomialCoeffs::empty();
|
|
|
|
|
|
2021-07-01 18:24:49 +02:00
|
|
|
let mut zs_polys = commitments[PlonkPolynomials::ZS_PARTIAL_PRODUCTS.index]
|
|
|
|
|
.polynomials
|
|
|
|
|
.iter()
|
|
|
|
|
.collect::<Vec<_>>();
|
|
|
|
|
let partial_products_polys = zs_polys.split_off(common_data.zs_range().end);
|
|
|
|
|
|
2021-06-01 11:17:54 +02:00
|
|
|
// Polynomials opened at a single point.
|
2021-06-17 21:34:04 +02:00
|
|
|
let single_polys = [
|
2021-06-25 11:24:26 +02:00
|
|
|
PlonkPolynomials::CONSTANTS_SIGMAS,
|
2021-07-19 16:45:38 +02:00
|
|
|
PlonkPolynomials::WIRES,
|
2021-06-17 21:34:04 +02:00
|
|
|
PlonkPolynomials::QUOTIENT,
|
|
|
|
|
]
|
|
|
|
|
.iter()
|
|
|
|
|
.flat_map(|&p| &commitments[p.index].polynomials)
|
2021-07-01 18:24:49 +02:00
|
|
|
.chain(partial_products_polys);
|
2021-08-02 15:49:06 -07:00
|
|
|
let single_composition_poly = timed!(
|
|
|
|
|
timing,
|
|
|
|
|
"reduce single polys",
|
2021-08-03 13:00:50 -07:00
|
|
|
alpha.reduce_polys_base(single_polys)
|
2021-08-02 15:49:06 -07:00
|
|
|
);
|
2021-06-23 11:30:57 +02:00
|
|
|
|
|
|
|
|
let single_quotient = Self::compute_quotient([zeta], single_composition_poly);
|
|
|
|
|
final_poly += single_quotient;
|
2021-06-17 21:34:04 +02:00
|
|
|
alpha.reset();
|
2021-05-31 17:49:04 +02:00
|
|
|
|
2021-06-01 11:17:54 +02:00
|
|
|
// Zs polynomials are opened at `zeta` and `g*zeta`.
|
2021-08-02 15:49:06 -07:00
|
|
|
let zs_composition_poly = timed!(
|
|
|
|
|
timing,
|
|
|
|
|
"reduce Z polys",
|
2021-08-03 13:00:50 -07:00
|
|
|
alpha.reduce_polys_base(zs_polys.into_iter())
|
2021-08-02 15:49:06 -07:00
|
|
|
);
|
2021-05-31 17:49:04 +02:00
|
|
|
|
2021-06-23 11:30:57 +02:00
|
|
|
let zs_quotient = Self::compute_quotient([zeta, g * zeta], zs_composition_poly);
|
2021-06-23 11:41:32 +02:00
|
|
|
alpha.shift_poly(&mut final_poly);
|
2021-06-23 11:30:57 +02:00
|
|
|
final_poly += zs_quotient;
|
2021-05-31 17:49:04 +02:00
|
|
|
|
|
|
|
|
let lde_final_poly = final_poly.lde(config.rate_bits);
|
2021-08-02 15:49:06 -07:00
|
|
|
let lde_final_values = timed!(
|
|
|
|
|
timing,
|
|
|
|
|
&format!("perform final FFT {}", lde_final_poly.len()),
|
|
|
|
|
lde_final_poly.coset_fft(F::coset_shift().into())
|
|
|
|
|
);
|
2021-05-06 17:09:55 +02:00
|
|
|
|
|
|
|
|
let fri_proof = fri_proof(
|
|
|
|
|
&commitments
|
2021-05-07 16:49:27 +02:00
|
|
|
.par_iter()
|
2021-05-06 17:09:55 +02:00
|
|
|
.map(|c| &c.merkle_tree)
|
|
|
|
|
.collect::<Vec<_>>(),
|
2021-07-21 08:26:56 -07:00
|
|
|
lde_final_poly,
|
|
|
|
|
lde_final_values,
|
2021-05-06 17:09:55 +02:00
|
|
|
challenger,
|
Automatically select FRI reduction arities (#282)
* Automatically select FRI reduction arities
This way when a proof's degree changes, we won't need to manually update the `FriConfig`s of any recursive proofs on top of it.
For now I've added two methods of selecting arities. The first, `ConstantArityBits`, just applies a fixed reduciton arity until the degree has shrunk below a certain threshold. The second, `MinSize`, searches for the sequence of arities that minimizes proof size.
Note that this optimization is approximate -- e.g. it doesn't account for the effect of compression, and doesn't count some minor contributions to proof size, like the Merkle roots from the commit phase. It also assumes we're not using Merkle caps in serialized proofs, and that we're inferring one of the evaluations, even though we haven't made those changes yet.
I think we should generally use `ConstantArityBits` for proofs that we will recurse on, since using a single arity tends to be more recursion-friendly. We could use `MinSize` for generating final bridge proofs, since we won't do further recursion on top of those.
* Fix tests
* Feedback
2021-10-04 13:52:05 -07:00
|
|
|
&common_data,
|
2021-08-02 10:38:09 -07:00
|
|
|
timing,
|
2021-05-06 17:09:55 +02:00
|
|
|
);
|
|
|
|
|
|
2021-08-08 09:14:07 -07:00
|
|
|
(fri_proof, os)
|
2021-05-06 17:09:55 +02:00
|
|
|
}
|
|
|
|
|
|
2021-05-05 18:32:24 +02:00
|
|
|
/// Given `points=(x_i)`, `evals=(y_i)` and `poly=P` with `P(x_i)=y_i`, computes the polynomial
|
|
|
|
|
/// `Q=(P-I)/Z` where `I` interpolates `(x_i, y_i)` and `Z` is the vanishing polynomial on `(x_i)`.
|
2021-06-23 11:30:57 +02:00
|
|
|
fn compute_quotient<const D: usize, const N: usize>(
|
|
|
|
|
points: [F::Extension; N],
|
|
|
|
|
poly: PolynomialCoeffs<F::Extension>,
|
2021-05-18 15:22:06 +02:00
|
|
|
) -> PolynomialCoeffs<F::Extension>
|
|
|
|
|
where
|
2021-05-18 15:44:50 +02:00
|
|
|
F: Extendable<D>,
|
2021-05-18 15:22:06 +02:00
|
|
|
{
|
2021-06-23 11:30:57 +02:00
|
|
|
let quotient = if N == 1 {
|
|
|
|
|
poly.divide_by_linear(points[0]).0
|
|
|
|
|
} else if N == 2 {
|
|
|
|
|
// The denominator is `(X - p0)(X - p1) = p0 p1 - (p0 + p1) X + X^2`.
|
|
|
|
|
let denominator = vec![
|
|
|
|
|
points[0] * points[1],
|
|
|
|
|
-points[0] - points[1],
|
|
|
|
|
F::Extension::ONE,
|
|
|
|
|
]
|
|
|
|
|
.into();
|
|
|
|
|
poly.div_rem_long_division(&denominator).0 // Could also use `divide_by_linear` twice.
|
|
|
|
|
} else {
|
|
|
|
|
unreachable!("This shouldn't happen. Plonk should open polynomials at 1 or 2 points.")
|
|
|
|
|
};
|
2021-05-05 18:32:24 +02:00
|
|
|
|
2021-05-10 12:58:58 -07:00
|
|
|
quotient.padded(quotient.degree_plus_one().next_power_of_two())
|
2021-05-05 18:32:24 +02:00
|
|
|
}
|
2021-05-03 15:17:05 +02:00
|
|
|
}
|