diff --git a/src/poly/multiopen.rs b/src/poly/multiopen.rs index ad702bd7..77a2502f 100644 --- a/src/poly/multiopen.rs +++ b/src/poly/multiopen.rs @@ -3,6 +3,8 @@ //! //! [halo]: https://eprint.iacr.org/2019/1021 +use std::collections::{BTreeMap, BTreeSet}; + use super::*; use crate::arithmetic::CurveAffine; @@ -45,3 +47,297 @@ pub struct VerifierQuery<'a, C: CurveAffine> { /// evaluation of polynomial at query point pub eval: C::Scalar, } + +struct CommitmentData { + commitment: T, + set_index: usize, + point_indices: Vec, + evals: Vec, +} + +impl CommitmentData { + fn new(commitment: T) -> Self { + CommitmentData { + commitment, + set_index: 0, + point_indices: vec![], + evals: vec![], + } + } +} + +trait Query: Sized { + type Commitment: PartialEq + Copy; + + fn get_point(&self) -> F; + fn get_eval(&self) -> F; + fn get_commitment(&self) -> Self::Commitment; +} + +fn construct_intermediate_sets>( + queries: I, +) -> (Vec>, Vec>) +where + I: IntoIterator + Clone, +{ + // Construct sets of unique commitments and corresponding information about + // their queries. + let mut commitment_map: Vec> = vec![]; + + // Also construct mapping from a unique point to a point_index. This defines + // an ordering on the points. + let mut point_index_map = BTreeMap::new(); + + // Iterate over all of the queries, computing the ordering of the points + // while also creating new commitment data. + for query in queries.clone() { + let num_points = point_index_map.len(); + let point_idx = point_index_map + .entry(query.get_point()) + .or_insert(num_points); + + if let Some(pos) = commitment_map + .iter() + .position(|comm| comm.commitment == query.get_commitment()) + { + commitment_map[pos].point_indices.push(*point_idx); + } else { + let mut tmp = CommitmentData::new(query.get_commitment()); + tmp.point_indices.push(*point_idx); + commitment_map.push(tmp); + } + } + + // Also construct inverse mapping from point_index to the point + let mut inverse_point_index_map = BTreeMap::new(); + for (&point, &point_index) in point_index_map.iter() { + inverse_point_index_map.insert(point_index, point); + } + + // Construct map of unique ordered point_idx_sets to their set_idx + let mut point_idx_sets = BTreeMap::new(); + // Also construct mapping from commitment to point_idx_set + let mut commitment_set_map = Vec::new(); + + for commitment_data in commitment_map.iter() { + let mut point_index_set = BTreeSet::new(); + // Note that point_index_set is ordered, unlike point_indices + for &point_index in commitment_data.point_indices.iter() { + point_index_set.insert(point_index); + } + + // Push point_index_set to CommitmentData for the relevant commitment + commitment_set_map.push((commitment_data.commitment, point_index_set.clone())); + + let num_sets = point_idx_sets.len(); + point_idx_sets.entry(point_index_set).or_insert(num_sets); + } + + // Initialise empty evals vec for each unique commitment + for commitment_data in commitment_map.iter_mut() { + let len = commitment_data.point_indices.len(); + commitment_data.evals = vec![F::zero(); len]; + } + + // Populate set_index, evals and points for each commitment using point_idx_sets + for query in queries { + // The index of the point at which the commitment is queried + let point_index = point_index_map.get(&query.get_point()).unwrap(); + + // The point_index_set at which the commitment was queried + let mut point_index_set = BTreeSet::new(); + for (commitment, point_idx_set) in commitment_set_map.iter() { + if query.get_commitment() == *commitment { + point_index_set = point_idx_set.clone(); + } + } + assert!(!point_index_set.is_empty()); + + // The set_index of the point_index_set + let set_index = point_idx_sets.get(&point_index_set).unwrap(); + for commitment_data in commitment_map.iter_mut() { + if query.get_commitment() == commitment_data.commitment { + commitment_data.set_index = *set_index; + } + } + let point_index_set: Vec = point_index_set.iter().cloned().collect(); + + // The offset of the point_index in the point_index_set + let point_index_in_set = point_index_set + .iter() + .position(|i| i == point_index) + .unwrap(); + + for commitment_data in commitment_map.iter_mut() { + if query.get_commitment() == commitment_data.commitment { + // Insert the eval using the ordering of the point_index_set + commitment_data.evals[point_index_in_set] = query.get_eval(); + } + } + } + + // Get actual points in each point set + let mut point_sets: Vec> = vec![Vec::new(); point_idx_sets.len()]; + for (point_idx_set, &set_idx) in point_idx_sets.iter() { + for &point_idx in point_idx_set.iter() { + let point = inverse_point_index_map.get(&point_idx).unwrap(); + point_sets[set_idx].push(*point); + } + } + + (commitment_map, point_sets) +} + +#[cfg(test)] +mod tests { + use super::{construct_intermediate_sets, Query}; + use crate::arithmetic::{Field, Fp}; + + #[derive(Clone)] + struct MyQuery { + commitment: usize, + point: F, + eval: F, + } + + impl Query for MyQuery { + type Commitment = usize; + + fn get_point(&self) -> F { + self.point + } + fn get_eval(&self) -> F { + self.eval + } + fn get_commitment(&self) -> Self::Commitment { + self.commitment + } + } + + #[test] + fn test_coherence() { + let points = &[ + Fp::random(), + Fp::random(), + Fp::random(), + Fp::random(), + Fp::random(), + ]; + + let queries = vec![ + MyQuery { + commitment: 0, + point: points[0], + eval: Fp::random(), + }, + MyQuery { + commitment: 0, + point: points[1], + eval: Fp::random(), + }, + MyQuery { + commitment: 1, + point: points[0], + eval: Fp::random(), + }, + MyQuery { + commitment: 1, + point: points[1], + eval: Fp::random(), + }, + MyQuery { + commitment: 2, + point: points[0], + eval: Fp::random(), + }, + MyQuery { + commitment: 2, + point: points[1], + eval: Fp::random(), + }, + MyQuery { + commitment: 2, + point: points[2], + eval: Fp::random(), + }, + MyQuery { + commitment: 3, + point: points[0], + eval: Fp::random(), + }, + MyQuery { + commitment: 3, + point: points[3], + eval: Fp::random(), + }, + MyQuery { + commitment: 4, + point: points[4], + eval: Fp::random(), + }, + ]; + + let (commitment_data, point_sets) = construct_intermediate_sets(queries); + + let mut a = false; + let mut a_set = 0; + let mut b = false; + let mut b_set = 0; + let mut c = false; + let mut c_set = 0; + let mut d = false; + let mut d_set = 0; + + for (i, mut point_set) in point_sets.into_iter().enumerate() { + point_set.sort(); + if point_set.len() == 1 { + assert_eq!(point_set[0], points[4]); + assert!(!a); + a = true; + a_set = i; + } else if point_set.len() == 2 { + let mut v0 = [points[0], points[1]]; + let mut v1 = [points[0], points[3]]; + v0.sort(); + v1.sort(); + + if &point_set[..] == &v0[..] { + assert!(!b); + b = true; + b_set = i; + } else if &point_set[..] == &v1[..] { + assert!(!c); + c = true; + c_set = i; + } else { + panic!("unexpected"); + } + } else if point_set.len() == 3 { + let mut v = [points[0], points[1], points[2]]; + v.sort(); + assert_eq!(&point_set[..], &v[..]); + assert!(!d); + d = true; + d_set = i; + } else { + panic!("unexpected"); + } + } + + assert!(a & b & c & d); + + for commitment_data in commitment_data { + assert_eq!( + commitment_data.set_index, + match commitment_data.commitment { + 0 => b_set, + 1 => b_set, + 2 => d_set, + 3 => c_set, + 4 => a_set, + _ => unreachable!(), + } + ); + } + } +} diff --git a/src/poly/multiopen/prover.rs b/src/poly/multiopen/prover.rs index 654dbd1c..5a293d31 100644 --- a/src/poly/multiopen/prover.rs +++ b/src/poly/multiopen/prover.rs @@ -2,7 +2,7 @@ use super::super::{ commitment::{self, Blind, Params}, Coeff, Error, Polynomial, }; -use super::{Proof, ProverQuery}; +use super::{construct_intermediate_sets, Proof, ProverQuery, Query}; use crate::arithmetic::{ eval_polynomial, get_challenge_scalar, kate_division, lagrange_interpolate, Challenge, Curve, @@ -10,7 +10,6 @@ use crate::arithmetic::{ }; use crate::plonk::hash_point; use crate::transcript::Hasher; -use std::collections::{BTreeMap, BTreeSet}; use std::marker::PhantomData; #[derive(Debug, Clone)] @@ -34,7 +33,7 @@ impl Proof { { let x_4: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128())); - let (poly_map, point_sets) = construct_intermediate_sets::<'a, C, I>(queries); + let (poly_map, point_sets) = construct_intermediate_sets(queries); // Collapse openings at same point sets together into single openings using // x_4 challenge. @@ -68,12 +67,12 @@ impl Proof { } }; - for (poly, commitment_data) in poly_map { + for commitment_data in poly_map { accumulate( - commitment_data.set_index, // set_idx, - &poly, // poly, - commitment_data.blind, // blind, - commitment_data.evals.to_vec(), // evals + commitment_data.set_index, // set_idx, + commitment_data.commitment.poly, // poly, + commitment_data.commitment.blind, // blind, + commitment_data.evals.to_vec(), // evals ); } } @@ -162,126 +161,32 @@ impl Proof { } } -// For multiopen prover: Construct intermediate representations relating polynomials to sets of points by index -fn construct_intermediate_sets<'a, C: CurveAffine, I>( - queries: I, -) -> ( - Vec<(&'a Polynomial, CommitmentData)>, // poly_map - Vec>, // point_sets -) -where - I: IntoIterator> + Clone, -{ - // Construct vec of unique polynomials and corresponding information about their queries - let mut poly_map: Vec<(&'a Polynomial, CommitmentData)> = Vec::new(); - - // Also construct mapping from a unique point to a point_index - let mut point_index_map: BTreeMap = BTreeMap::new(); - - // Construct point_indices which each polynomial is queried at - for query in queries.clone() { - let num_points = point_index_map.len(); - let point_idx = point_index_map.entry(query.point).or_insert(num_points); - - let mut exists = false; - for (existing_poly, existing_commitment_data) in poly_map.iter_mut() { - // Add to CommitmentData for existing commitment in commitment_map - if std::ptr::eq(query.poly, *existing_poly) { - exists = true; - existing_commitment_data.point_indices.push(*point_idx); - } - } - - // Add new poly and CommitmentData to poly_map - if !exists { - let commitment_data = CommitmentData { - set_index: 0, - blind: query.blind, - point_indices: vec![*point_idx], - evals: vec![], - }; - poly_map.push((query.poly, commitment_data)); - } - } - - // Also construct inverse mapping from point_index to the point - let mut inverse_point_index_map: BTreeMap = BTreeMap::new(); - for (&point, &point_index) in point_index_map.iter() { - inverse_point_index_map.insert(point_index, point); - } - - // Construct map of unique ordered point_idx_sets to their set_idx - let mut point_idx_sets: BTreeMap, usize> = BTreeMap::new(); - // Also construct mapping from poly to point_idx_set - let mut poly_set_map: Vec<(&Polynomial, BTreeSet)> = Vec::new(); - - for (poly, commitment_data) in poly_map.iter_mut() { - let mut point_index_set = BTreeSet::new(); - // Note that point_index_set is ordered, unlike point_indices - for &point_index in commitment_data.point_indices.iter() { - point_index_set.insert(point_index); - } - - // Push point_index_set to CommitmentData for the relevant poly - poly_set_map.push((poly, point_index_set.clone())); - - let num_sets = point_idx_sets.len(); - point_idx_sets - .entry(point_index_set.clone()) - .or_insert(num_sets); - } - - // Initialise empty evals vec for each unique poly - for (_, commitment_data) in poly_map.iter_mut() { - let len = commitment_data.point_indices.len(); - commitment_data.evals = vec![C::Scalar::zero(); len]; - } - - // Populate set_index, evals and points for each poly using point_idx_sets - for query in queries.clone() { - // The index of the point at which the poly is queried - let point_index = point_index_map.get(&query.point).unwrap(); - - // The point_index_set at which the poly was queried - let mut point_index_set = BTreeSet::new(); - for (poly, point_idx_set) in poly_set_map.iter() { - if std::ptr::eq(query.poly, *poly) { - point_index_set = point_idx_set.clone(); - } - } - - // The set_index of the point_index_set - let set_index = point_idx_sets.get(&point_index_set).unwrap(); - for (poly, commitment_data) in poly_map.iter_mut() { - if std::ptr::eq(query.poly, *poly) { - commitment_data.set_index = *set_index; - } - } - - let point_index_set: Vec = point_index_set.iter().cloned().collect(); - - // The offset of the point_index in the point_index_set - let point_index_in_set = point_index_set - .iter() - .position(|i| i == point_index) - .unwrap(); - - for (poly, commitment_data) in poly_map.iter_mut() { - if std::ptr::eq(query.poly, *poly) { - // Insert the eval using the ordering of the point_index_set - commitment_data.evals[point_index_in_set] = query.eval; - } - } - } - - // Get actual points in each point set - let mut point_sets: Vec> = vec![Vec::new(); point_idx_sets.len()]; - for (point_idx_set, &set_idx) in point_idx_sets.iter() { - for &point_idx in point_idx_set.iter() { - let point = inverse_point_index_map.get(&point_idx).unwrap(); - point_sets[set_idx].push(*point); - } - } - - (poly_map, point_sets) +#[doc(hidden)] +#[derive(Copy, Clone)] +pub struct PolynomialPointer<'a, C: CurveAffine> { + poly: &'a Polynomial, + blind: commitment::Blind, +} + +impl<'a, C: CurveAffine> PartialEq for PolynomialPointer<'a, C> { + fn eq(&self, other: &Self) -> bool { + std::ptr::eq(self.poly, other.poly) + } +} + +impl<'a, C: CurveAffine> Query for ProverQuery<'a, C> { + type Commitment = PolynomialPointer<'a, C>; + + fn get_point(&self) -> C::Scalar { + self.point + } + fn get_eval(&self) -> C::Scalar { + self.eval + } + fn get_commitment(&self) -> Self::Commitment { + PolynomialPointer { + poly: self.poly, + blind: self.blind, + } + } } diff --git a/src/poly/multiopen/verifier.rs b/src/poly/multiopen/verifier.rs index 998ceb0a..b2132603 100644 --- a/src/poly/multiopen/verifier.rs +++ b/src/poly/multiopen/verifier.rs @@ -2,13 +2,12 @@ use super::super::{ commitment::{Guard, Params, MSM}, Error, }; -use super::{Proof, VerifierQuery}; +use super::{construct_intermediate_sets, Proof, Query, VerifierQuery}; use crate::arithmetic::{ eval_polynomial, get_challenge_scalar, lagrange_interpolate, Challenge, CurveAffine, Field, }; use crate::plonk::hash_point; use crate::transcript::Hasher; -use std::collections::{BTreeMap, BTreeSet}; #[derive(Debug, Clone)] struct CommitmentData { @@ -38,7 +37,7 @@ impl Proof { // Sample x_4 for compressing openings at the same points together let x_4: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128())); - let (commitment_map, point_sets) = construct_intermediate_sets::<'a, C, I>(queries.clone()); + let (commitment_map, point_sets) = construct_intermediate_sets(queries); // Compress the commitments and expected evaluations at x_3 together. // using the challenge x_4 @@ -62,11 +61,11 @@ impl Proof { // Each commitment corresponds to evaluations at a set of points. // For each set, we collapse each commitment's evals pointwise. - for (commitment, commitment_data) in commitment_map.into_iter() { + for commitment_data in commitment_map.into_iter() { accumulate( - commitment_data.set_index, // set_idx, - *commitment, // commitment, - commitment_data.evals, // evals + commitment_data.set_index, // set_idx, + *commitment_data.commitment.0, // commitment, + commitment_data.evals, // evals ); } } @@ -130,123 +129,26 @@ impl Proof { } } -// For multiopen verifier: Construct intermediate representations relating commitments to sets of points by index -fn construct_intermediate_sets<'a, C: CurveAffine, I>( - queries: I, -) -> ( - Vec<(&'a C, CommitmentData)>, // commitment_map - Vec>, // point_sets -) -where - I: IntoIterator> + Clone, -{ - // Construct sets of unique commitments and corresponding information about their queries - let mut commitment_map: Vec<(&'a C, CommitmentData)> = Vec::new(); +#[doc(hidden)] +#[derive(Copy, Clone)] +pub struct CommitmentPointer<'a, C>(&'a C); - // Also construct mapping from a unique point to a point_index. This defines an ordering on the points. - let mut point_index_map: BTreeMap = BTreeMap::new(); - - // Construct point_indices which each commitment is queried at - for query in queries.clone() { - let num_points = point_index_map.len(); - let point_idx = point_index_map.entry(query.point).or_insert(num_points); - - let mut exists = false; - for (existing_commitment, existing_commitment_data) in commitment_map.iter_mut() { - // Add to CommitmentData for existing commitment in commitment_map - if std::ptr::eq(query.commitment, *existing_commitment) { - exists = true; - existing_commitment_data.point_indices.push(*point_idx); - } - } - - // Add new commitment and CommitmentData to commitment_map - if !exists { - let commitment_data = CommitmentData { - set_index: 0, - point_indices: vec![*point_idx], - evals: vec![], - }; - commitment_map.push((query.commitment, commitment_data)); - } +impl<'a, C> PartialEq for CommitmentPointer<'a, C> { + fn eq(&self, other: &Self) -> bool { + std::ptr::eq(self.0, other.0) + } +} + +impl<'a, C: CurveAffine> Query for VerifierQuery<'a, C> { + type Commitment = CommitmentPointer<'a, C>; + + fn get_point(&self) -> C::Scalar { + self.point + } + fn get_eval(&self) -> C::Scalar { + self.eval + } + fn get_commitment(&self) -> Self::Commitment { + CommitmentPointer(self.commitment) } - - // Also construct inverse mapping from point_index to the point - let mut inverse_point_index_map: BTreeMap = BTreeMap::new(); - for (&point, &point_index) in point_index_map.iter() { - inverse_point_index_map.insert(point_index, point); - } - - // Construct map of unique ordered point_idx_sets to their set_idx - let mut point_idx_sets: BTreeMap, usize> = BTreeMap::new(); - // Also construct mapping from commitment to point_idx_set - let mut commitment_set_map: Vec<(&'a C, BTreeSet)> = Vec::new(); - - for (commitment, commitment_data) in commitment_map.iter_mut() { - let mut point_index_set = BTreeSet::new(); - // Note that point_index_set is ordered, unlike point_indices - for &point_index in commitment_data.point_indices.iter() { - point_index_set.insert(point_index); - } - - // Push point_index_set to CommitmentData for the relevant commitment - commitment_set_map.push((commitment, point_index_set.clone())); - - let num_sets = point_idx_sets.len(); - point_idx_sets - .entry(point_index_set.clone()) - .or_insert(num_sets); - } - - // Initialise empty evals vec for each unique commitment - for (_, commitment_data) in commitment_map.iter_mut() { - let len = commitment_data.point_indices.len(); - commitment_data.evals = vec![C::Scalar::zero(); len]; - } - - // Populate set_index, evals and points for each commitment using point_idx_sets - for query in queries.clone() { - // The index of the point at which the commitment is queried - let point_index = point_index_map.get(&query.point).unwrap(); - - // The point_index_set at which the commitment was queried - let mut point_index_set = BTreeSet::new(); - for (commitment, point_idx_set) in commitment_set_map.iter() { - if std::ptr::eq(query.commitment, *commitment) { - point_index_set = point_idx_set.clone(); - } - } - // The set_index of the point_index_set - let set_index = point_idx_sets.get(&point_index_set).unwrap(); - for (commitment, commitment_data) in commitment_map.iter_mut() { - if std::ptr::eq(query.commitment, *commitment) { - commitment_data.set_index = *set_index; - } - } - let point_index_set: Vec = point_index_set.iter().cloned().collect(); - - // The offset of the point_index in the point_index_set - let point_index_in_set = point_index_set - .iter() - .position(|i| i == point_index) - .unwrap(); - - for (commitment, commitment_data) in commitment_map.iter_mut() { - if std::ptr::eq(query.commitment, *commitment) { - // Insert the eval using the ordering of the point_index_set - commitment_data.evals[point_index_in_set] = query.eval; - } - } - } - - // Get actual points in each point set - let mut point_sets: Vec> = vec![Vec::new(); point_idx_sets.len()]; - for (point_idx_set, &set_idx) in point_idx_sets.iter() { - for &point_idx in point_idx_set.iter() { - let point = inverse_point_index_map.get(&point_idx).unwrap(); - point_sets[set_idx].push(*point); - } - } - - (commitment_map, point_sets) }