halo2_axiom/poly/kzg/multiopen/
shplonk.rs

1mod prover;
2mod verifier;
3
4use std::hash::Hash;
5
6use crate::multicore::IntoParallelIterator;
7use crate::{poly::query::Query, transcript::ChallengeScalar};
8use ff::Field;
9pub use prover::ProverSHPLONK;
10use rustc_hash::FxHashSet;
11pub use verifier::VerifierSHPLONK;
12
13#[cfg(feature = "multicore")]
14use crate::multicore::ParallelIterator;
15
16#[derive(Clone, Copy, Debug)]
17struct U {}
18type ChallengeU<F> = ChallengeScalar<F, U>;
19
20#[derive(Clone, Copy, Debug)]
21struct V {}
22type ChallengeV<F> = ChallengeScalar<F, V>;
23
24#[derive(Clone, Copy, Debug)]
25struct Y {}
26type ChallengeY<F> = ChallengeScalar<F, Y>;
27
28#[derive(Debug, Clone, PartialEq)]
29struct Commitment<F: Field, T: PartialEq + Clone>((T, Vec<F>));
30
31impl<F: Field, T: PartialEq + Clone> Commitment<F, T> {
32    fn get(&self) -> T {
33        self.0 .0.clone()
34    }
35
36    fn evals(&self) -> Vec<F> {
37        self.0 .1.clone()
38    }
39}
40
41#[derive(Debug, Clone, PartialEq)]
42struct RotationSet<F: Field, T: PartialEq + Clone> {
43    commitments: Vec<Commitment<F, T>>,
44    points: Vec<F>,
45}
46
47#[derive(Debug, PartialEq)]
48struct IntermediateSets<F: Field + Hash, Q: Query<F>> {
49    rotation_sets: Vec<RotationSet<F, Q::Commitment>>,
50    super_point_set: FxHashSet<F>,
51}
52
53fn construct_intermediate_sets<F: Field + Hash, I, Q: Query<F, Eval = F>>(
54    queries: I,
55) -> IntermediateSets<F, Q>
56where
57    I: IntoIterator<Item = Q> + Clone,
58{
59    let queries = queries.into_iter().collect::<Vec<_>>();
60
61    // Find evaluation of a commitment at a rotation
62    let get_eval = |commitment: Q::Commitment, rotation: F| -> F {
63        queries
64            .iter()
65            .find(|query| query.get_commitment() == commitment && query.get_point() == rotation)
66            .unwrap()
67            .get_eval()
68    };
69
70    // All points that appear in queries
71    let mut super_point_set = FxHashSet::default();
72
73    // Collect rotation sets for each commitment
74    // Example elements in the vector:
75    // (C_0, {r_5}),
76    // (C_1, {r_1, r_2, r_3}),
77    // (C_2, {r_2, r_3, r_4}),
78    // (C_3, {r_2, r_3, r_4}),
79    // ...
80    let mut commitment_rotation_set_map: Vec<(Q::Commitment, FxHashSet<F>)> = vec![];
81    for query in queries.iter() {
82        let rotation = query.get_point();
83        super_point_set.insert(rotation);
84        if let Some(commitment_rotation_set) = commitment_rotation_set_map
85            .iter_mut()
86            .find(|(commitment, _)| *commitment == query.get_commitment())
87        {
88            let (_, rotation_set) = commitment_rotation_set;
89            rotation_set.insert(rotation);
90        } else {
91            commitment_rotation_set_map.push((
92                query.get_commitment(),
93                FxHashSet::from_iter(core::iter::once(rotation)),
94            ));
95        };
96    }
97
98    // Flatten rotation sets and collect commitments that opens against each commitment set
99    // Example elements in the vector:
100    // {r_5}: [C_0],
101    // {r_1, r_2, r_3} : [C_1]
102    // {r_2, r_3, r_4} : [C_2, C_3],
103    // ...
104    let mut rotation_set_commitment_map: Vec<(FxHashSet<F>, Vec<Q::Commitment>)> = vec![];
105    for (commitment, rotation_set) in commitment_rotation_set_map.into_iter() {
106        if let Some(rotation_set_commitment) = rotation_set_commitment_map
107            .iter_mut()
108            .find(|(set, _)| set == &rotation_set)
109        {
110            let (_, commitments) = rotation_set_commitment;
111            commitments.push(commitment);
112        } else {
113            rotation_set_commitment_map.push((rotation_set, vec![commitment]));
114        };
115    }
116
117    let rotation_sets = rotation_set_commitment_map
118        .into_par_iter()
119        .map(|(rotations, commitments)| {
120            let rotations_vec = rotations.iter().collect::<Vec<_>>();
121            let commitments: Vec<Commitment<F, Q::Commitment>> = commitments
122                .into_par_iter()
123                .map(|commitment| {
124                    let evals: Vec<F> = rotations_vec
125                        .as_slice()
126                        .into_par_iter()
127                        .map(|&&rotation| get_eval(commitment, rotation))
128                        .collect();
129                    Commitment((commitment, evals))
130                })
131                .collect();
132
133            RotationSet {
134                commitments,
135                points: rotations.into_iter().collect(),
136            }
137        })
138        .collect::<Vec<RotationSet<_, _>>>();
139
140    IntermediateSets {
141        rotation_sets,
142        super_point_set,
143    }
144}
145
146#[cfg(test)]
147mod proptests {
148    use super::{construct_intermediate_sets, Commitment, IntermediateSets};
149    use ff::FromUniformBytes;
150    use halo2curves::bn256::Fr;
151    use proptest::{collection::vec, prelude::*, sample::select};
152    use std::convert::TryFrom;
153
154    #[derive(Debug, Clone)]
155    struct MyQuery<F> {
156        point: F,
157        eval: F,
158        commitment: usize,
159    }
160
161    impl super::Query<Fr> for MyQuery<Fr> {
162        type Commitment = usize;
163        type Eval = Fr;
164
165        fn get_point(&self) -> Fr {
166            self.point
167        }
168
169        fn get_eval(&self) -> Self::Eval {
170            self.eval
171        }
172
173        fn get_commitment(&self) -> Self::Commitment {
174            self.commitment
175        }
176    }
177
178    prop_compose! {
179        fn arb_point()(
180            bytes in vec(any::<u8>(), 64)
181        ) -> Fr {
182            Fr::from_uniform_bytes(&<[u8; 64]>::try_from(bytes).unwrap())
183        }
184    }
185
186    prop_compose! {
187        fn arb_query(commitment: usize, point: Fr)(
188            eval in arb_point()
189        ) -> MyQuery<Fr> {
190            MyQuery {
191                point,
192                eval,
193                commitment
194            }
195        }
196    }
197
198    prop_compose! {
199        // Mapping from column index to point index.
200        fn arb_queries_inner(num_points: usize, num_cols: usize, num_queries: usize)(
201            col_indices in vec(select((0..num_cols).collect::<Vec<_>>()), num_queries),
202            point_indices in vec(select((0..num_points).collect::<Vec<_>>()), num_queries)
203        ) -> Vec<(usize, usize)> {
204            col_indices.into_iter().zip(point_indices).collect()
205        }
206    }
207
208    prop_compose! {
209        fn compare_queries(
210            num_points: usize,
211            num_cols: usize,
212            num_queries: usize,
213        )(
214            points_1 in vec(arb_point(), num_points),
215            points_2 in vec(arb_point(), num_points),
216            mapping in arb_queries_inner(num_points, num_cols, num_queries)
217        )(
218            queries_1 in mapping.iter().map(|(commitment, point_idx)| arb_query(*commitment, points_1[*point_idx])).collect::<Vec<_>>(),
219            queries_2 in mapping.iter().map(|(commitment, point_idx)| arb_query(*commitment, points_2[*point_idx])).collect::<Vec<_>>(),
220        ) -> (
221            Vec<MyQuery<Fr>>,
222            Vec<MyQuery<Fr>>
223        ) {
224            (
225                queries_1,
226                queries_2,
227            )
228        }
229    }
230
231    proptest! {
232        #[test]
233        fn test_intermediate_sets(
234            (queries_1, queries_2) in compare_queries(8, 8, 16)
235        ) {
236            let IntermediateSets { rotation_sets, .. } = construct_intermediate_sets(queries_1);
237            let commitment_sets = rotation_sets.iter().map(|data|
238                data.commitments.iter().map(Commitment::get).collect::<Vec<_>>()
239            ).collect::<Vec<_>>();
240
241            // It shouldn't matter what the point or eval values are; we should get
242            // the same exact point set indices and point indices again.
243            let IntermediateSets { rotation_sets: new_rotation_sets, .. } = construct_intermediate_sets(queries_2);
244            let new_commitment_sets = new_rotation_sets.iter().map(|data|
245                data.commitments.iter().map(Commitment::get).collect::<Vec<_>>()
246            ).collect::<Vec<_>>();
247
248            assert_eq!(commitment_sets, new_commitment_sets);
249        }
250    }
251}