proof_of_sql/base/commitment/
vec_commitment_ext.rs

1use super::Commitment;
2use crate::base::commitment::committable_column::CommittableColumn;
3use alloc::vec::Vec;
4use snafu::Snafu;
5
6/// Cannot update commitment collections with different column counts
7#[derive(Snafu, Debug)]
8#[snafu(display("cannot update commitment collections with different column counts"))]
9pub struct NumColumnsMismatch;
10
11/// Extension trait intended for collections of commitments.
12///
13/// Implemented for `Vec<CompressedRistretto>`.
14pub trait VecCommitmentExt {
15    /// The public setup parameters required to compute the commitments.
16    /// This is simply precomputed data that is required to compute the commitments.
17    type CommitmentPublicSetup<'a>;
18
19    /// Returns a collection of commitments to the provided columns using the given generator offset.
20    fn from_columns_with_offset<'a, C>(
21        columns: impl IntoIterator<Item = C>,
22        offset: usize,
23        setup: &Self::CommitmentPublicSetup<'_>,
24    ) -> Self
25    where
26        C: Into<CommittableColumn<'a>>;
27
28    /// Returns a collection of commitments to the provided slice of `CommittableColumn`s using the given generator offset.
29    fn from_committable_columns_with_offset(
30        committable_columns: &[CommittableColumn],
31        offset: usize,
32        setup: &Self::CommitmentPublicSetup<'_>,
33    ) -> Self;
34
35    /// Append rows of data from the provided columns to the existing commitments.
36    ///
37    /// The given generator offset will be used for committing to the new rows.
38    /// You most likely want this to be equal to the 0-indexed row number of the first new row.
39    ///
40    /// The number of columns provided must match the number of columns already committed to.
41    fn try_append_rows_with_offset<'a, C>(
42        &mut self,
43        columns: impl IntoIterator<Item = C>,
44        offset: usize,
45        setup: &Self::CommitmentPublicSetup<'_>,
46    ) -> Result<(), NumColumnsMismatch>
47    where
48        C: Into<CommittableColumn<'a>>;
49
50    /// Add commitments to new columns to this collection using the given generator offset.
51    fn extend_columns_with_offset<'a, C>(
52        &mut self,
53        columns: impl IntoIterator<Item = C>,
54        offset: usize,
55        setup: &Self::CommitmentPublicSetup<'_>,
56    ) where
57        C: Into<CommittableColumn<'a>>;
58
59    /// Add two collections of commitments if they have equal column counts.
60    fn try_add(self, other: Self) -> Result<Self, NumColumnsMismatch>
61    where
62        Self: Sized;
63
64    /// Subtract two collections of commitments if they have equal column counts.
65    fn try_sub(self, other: Self) -> Result<Self, NumColumnsMismatch>
66    where
67        Self: Sized;
68
69    /// Returns the number of commitments in the collection.
70    fn num_commitments(&self) -> usize;
71}
72
73fn unsafe_add_assign<C: Commitment>(a: &mut [C], b: &[C]) {
74    a.iter_mut().zip(b).for_each(|(c_a, c_b)| {
75        *c_a += c_b.clone();
76    });
77}
78fn unsafe_sub_assign<C: Commitment>(a: &mut [C], b: &[C]) {
79    a.iter_mut().zip(b).for_each(|(c_a, c_b)| {
80        *c_a -= c_b.clone();
81    });
82}
83
84impl<C: Commitment> VecCommitmentExt for Vec<C> {
85    type CommitmentPublicSetup<'a> = C::PublicSetup<'a>;
86    fn from_columns_with_offset<'a, COL>(
87        columns: impl IntoIterator<Item = COL>,
88        offset: usize,
89        setup: &Self::CommitmentPublicSetup<'_>,
90    ) -> Self
91    where
92        COL: Into<CommittableColumn<'a>>,
93    {
94        let committable_columns: Vec<CommittableColumn<'a>> =
95            columns.into_iter().map(Into::into).collect::<Vec<_>>();
96
97        Self::from_committable_columns_with_offset(&committable_columns, offset, setup)
98    }
99
100    fn from_committable_columns_with_offset(
101        committable_columns: &[CommittableColumn],
102        offset: usize,
103        setup: &Self::CommitmentPublicSetup<'_>,
104    ) -> Self {
105        C::compute_commitments(committable_columns, offset, setup)
106    }
107
108    fn try_append_rows_with_offset<'a, COL>(
109        &mut self,
110        columns: impl IntoIterator<Item = COL>,
111        offset: usize,
112        setup: &Self::CommitmentPublicSetup<'_>,
113    ) -> Result<(), NumColumnsMismatch>
114    where
115        COL: Into<CommittableColumn<'a>>,
116    {
117        let committable_columns: Vec<CommittableColumn<'a>> =
118            columns.into_iter().map(Into::into).collect::<Vec<_>>();
119
120        if self.len() != committable_columns.len() {
121            return Err(NumColumnsMismatch);
122        }
123
124        let partial_commitments = C::compute_commitments(&committable_columns, offset, setup);
125        unsafe_add_assign(self, &partial_commitments);
126
127        Ok(())
128    }
129
130    fn extend_columns_with_offset<'a, COL>(
131        &mut self,
132        columns: impl IntoIterator<Item = COL>,
133        offset: usize,
134        setup: &Self::CommitmentPublicSetup<'_>,
135    ) where
136        COL: Into<CommittableColumn<'a>>,
137    {
138        self.extend(Self::from_columns_with_offset(columns, offset, setup));
139    }
140
141    fn try_add(self, other: Self) -> Result<Self, NumColumnsMismatch>
142    where
143        Self: Sized,
144    {
145        if self.len() != other.len() {
146            return Err(NumColumnsMismatch);
147        }
148
149        let mut commitments = self;
150        unsafe_add_assign(&mut commitments, &other);
151
152        Ok(commitments)
153    }
154
155    fn try_sub(self, other: Self) -> Result<Self, NumColumnsMismatch>
156    where
157        Self: Sized,
158    {
159        if self.len() != other.len() {
160            return Err(NumColumnsMismatch);
161        }
162
163        let mut commitments = self;
164        unsafe_sub_assign(&mut commitments, &other);
165
166        Ok(commitments)
167    }
168
169    fn num_commitments(&self) -> usize {
170        self.len()
171    }
172}
173
174#[cfg(all(test, feature = "blitzar"))]
175mod tests {
176    use super::*;
177    use crate::base::{
178        commitment::naive_commitment::NaiveCommitment,
179        database::{Column, OwnedColumn},
180        scalar::test_scalar::TestScalar,
181    };
182    #[test]
183    fn we_can_convert_from_columns() {
184        // empty case
185        let commitments = Vec::<NaiveCommitment>::from_columns_with_offset(
186            Vec::<Column<TestScalar>>::new(),
187            0,
188            &(),
189        );
190
191        assert!(commitments.is_empty());
192
193        // nonempty case
194        let column_a = [12i64, 34, 56];
195        let column_b = ["Lorem", "ipsum", "dolor"].map(String::from);
196
197        let columns = vec![
198            OwnedColumn::<TestScalar>::BigInt(column_a.to_vec()),
199            OwnedColumn::VarChar(column_b.to_vec()),
200        ];
201
202        let commitments = Vec::<NaiveCommitment>::from_columns_with_offset(&columns, 0, &());
203
204        let committable_columns = [
205            CommittableColumn::BigInt(&column_a),
206            CommittableColumn::VarChar(
207                column_b
208                    .iter()
209                    .map(TestScalar::from)
210                    .map(<[u64; 4]>::from)
211                    .collect(),
212            ),
213        ];
214        let expected_commitments =
215            NaiveCommitment::compute_commitments(&committable_columns, 0, &());
216        assert_eq!(commitments, expected_commitments);
217    }
218
219    #[test]
220    fn we_can_append_rows() {
221        let column_a = [12i64, 34, 56, 78, 90];
222        let column_b = ["Lorem", "ipsum", "dolor", "sit", "amet"].map(String::from);
223
224        let columns = vec![
225            OwnedColumn::<TestScalar>::BigInt(column_a[..3].to_vec()),
226            OwnedColumn::VarChar(column_b[..3].to_vec()),
227        ];
228
229        let mut commitments = Vec::<NaiveCommitment>::from_columns_with_offset(&columns, 0, &());
230
231        let new_columns = vec![
232            OwnedColumn::<TestScalar>::BigInt(column_a[3..].to_vec()),
233            OwnedColumn::VarChar(column_b[3..].to_vec()),
234        ];
235
236        commitments
237            .try_append_rows_with_offset(&new_columns, 3, &())
238            .unwrap();
239
240        let committable_columns = [
241            CommittableColumn::BigInt(&column_a),
242            CommittableColumn::VarChar(
243                column_b
244                    .iter()
245                    .map(TestScalar::from)
246                    .map(<[u64; 4]>::from)
247                    .collect(),
248            ),
249        ];
250        let expected_commitments =
251            NaiveCommitment::compute_commitments(&committable_columns, 0, &());
252        assert_eq!(commitments, expected_commitments);
253    }
254
255    #[test]
256    fn we_cannot_append_rows_with_different_column_count() {
257        let column_a = [12i64, 34, 56, 78, 90];
258        let column_b = ["Lorem", "ipsum", "dolor", "sit", "amet"].map(String::from);
259
260        let columns = vec![
261            OwnedColumn::<TestScalar>::BigInt(column_a[..3].to_vec()),
262            OwnedColumn::VarChar(column_b[..3].to_vec()),
263        ];
264
265        let mut commitments = Vec::<NaiveCommitment>::from_columns_with_offset(&columns, 0, &());
266
267        let new_columns = Vec::<Column<TestScalar>>::new();
268        assert!(matches!(
269            commitments.try_append_rows_with_offset(&new_columns, 3, &()),
270            Err(NumColumnsMismatch)
271        ));
272
273        let new_columns = vec![OwnedColumn::<TestScalar>::BigInt(column_a[3..].to_vec())];
274        assert!(matches!(
275            commitments.try_append_rows_with_offset(&new_columns, 3, &()),
276            Err(NumColumnsMismatch)
277        ));
278
279        let new_columns = vec![
280            OwnedColumn::<TestScalar>::BigInt(column_a[3..].to_vec()),
281            OwnedColumn::VarChar(column_b[3..].to_vec()),
282            OwnedColumn::BigInt(column_a[3..].to_vec()),
283        ];
284        assert!(matches!(
285            commitments.try_append_rows_with_offset(&new_columns, 3, &()),
286            Err(NumColumnsMismatch)
287        ));
288    }
289
290    #[test]
291    fn we_can_extend_columns() {
292        let column_a = [12i64, 34, 56];
293        let column_b = ["Lorem", "ipsum", "dolor"].map(String::from);
294        let column_c = ["sit", "amet", "consectetur"].map(String::from);
295        let column_d = [78i64, 90, 1112];
296
297        let columns = vec![
298            OwnedColumn::<TestScalar>::BigInt(column_a.to_vec()),
299            OwnedColumn::VarChar(column_b.to_vec()),
300        ];
301
302        let mut commitments = Vec::<NaiveCommitment>::from_columns_with_offset(&columns, 0, &());
303
304        let new_columns = vec![
305            OwnedColumn::<TestScalar>::VarChar(column_c.to_vec()),
306            OwnedColumn::BigInt(column_d.to_vec()),
307        ];
308
309        commitments.extend_columns_with_offset(&new_columns, 0, &());
310
311        let committable_columns = [
312            CommittableColumn::VarChar(
313                column_a
314                    .iter()
315                    .map(Into::<TestScalar>::into)
316                    .map(Into::<[u64; 4]>::into)
317                    .collect(),
318            ),
319            CommittableColumn::VarChar(
320                column_b
321                    .iter()
322                    .map(Into::<TestScalar>::into)
323                    .map(Into::<[u64; 4]>::into)
324                    .collect(),
325            ),
326            CommittableColumn::VarChar(
327                column_c
328                    .iter()
329                    .map(Into::<TestScalar>::into)
330                    .map(Into::<[u64; 4]>::into)
331                    .collect(),
332            ),
333            CommittableColumn::VarChar(
334                column_d
335                    .iter()
336                    .map(Into::<TestScalar>::into)
337                    .map(Into::<[u64; 4]>::into)
338                    .collect(),
339            ),
340        ];
341        let expected_commitments =
342            NaiveCommitment::compute_commitments(&committable_columns, 0, &());
343
344        assert_eq!(commitments, expected_commitments);
345    }
346
347    #[test]
348    fn we_can_add_commitment_collections() {
349        let column_a = [12i64, 34, 56, 78, 90];
350        let column_b = ["Lorem", "ipsum", "dolor", "sit", "amet"].map(String::from);
351
352        let columns = vec![
353            OwnedColumn::<TestScalar>::BigInt(column_a[..3].to_vec()),
354            OwnedColumn::VarChar(column_b[..3].to_vec()),
355        ];
356
357        let commitments_a = Vec::<NaiveCommitment>::from_columns_with_offset(&columns, 0, &());
358
359        let new_columns = vec![
360            OwnedColumn::<TestScalar>::BigInt(column_a[3..].to_vec()),
361            OwnedColumn::VarChar(column_b[3..].to_vec()),
362        ];
363
364        let commitments_b = Vec::<NaiveCommitment>::from_columns_with_offset(&new_columns, 3, &());
365
366        let commitments = commitments_a.try_add(commitments_b).unwrap();
367
368        let committable_columns = [
369            CommittableColumn::BigInt(&column_a),
370            CommittableColumn::VarChar(
371                column_b
372                    .iter()
373                    .map(Into::<TestScalar>::into)
374                    .map(Into::<[u64; 4]>::into)
375                    .collect(),
376            ),
377        ];
378
379        let expected_commitments =
380            NaiveCommitment::compute_commitments(&committable_columns, 0, &());
381
382        assert_eq!(commitments, expected_commitments);
383    }
384
385    #[test]
386    fn we_cannot_add_commitment_collections_of_mixed_column_counts() {
387        let column_a = [12i64, 34, 56, 78, 90];
388        let column_b = ["Lorem", "ipsum", "dolor", "sit", "amet"].map(String::from);
389
390        let columns = vec![
391            OwnedColumn::<TestScalar>::BigInt(column_a[..3].to_vec()),
392            OwnedColumn::VarChar(column_b[..3].to_vec()),
393        ];
394
395        let commitments = Vec::<NaiveCommitment>::from_columns_with_offset(&columns, 0, &());
396
397        let new_columns = Vec::<Column<TestScalar>>::new();
398        let new_commitments =
399            Vec::<NaiveCommitment>::from_columns_with_offset(&new_columns, 3, &());
400        assert!(matches!(
401            commitments.clone().try_add(new_commitments),
402            Err(NumColumnsMismatch)
403        ));
404
405        let new_columns = vec![OwnedColumn::<TestScalar>::BigInt(column_a[3..].to_vec())];
406        let new_commitments =
407            Vec::<NaiveCommitment>::from_columns_with_offset(&new_columns, 3, &());
408        assert!(matches!(
409            commitments.clone().try_add(new_commitments),
410            Err(NumColumnsMismatch)
411        ));
412
413        let new_columns = vec![
414            OwnedColumn::<TestScalar>::BigInt(column_a[3..].to_vec()),
415            OwnedColumn::VarChar(column_b[3..].to_vec()),
416            OwnedColumn::BigInt(column_a[3..].to_vec()),
417        ];
418        let new_commitments =
419            Vec::<NaiveCommitment>::from_columns_with_offset(&new_columns, 3, &());
420        assert!(matches!(
421            commitments.try_add(new_commitments),
422            Err(NumColumnsMismatch)
423        ));
424    }
425
426    #[test]
427    fn we_can_sub_commitment_collections() {
428        let column_a = [12i64, 34, 56, 78, 90];
429        let column_b = ["Lorem", "ipsum", "dolor", "sit", "amet"].map(String::from);
430
431        let columns = vec![
432            OwnedColumn::<TestScalar>::BigInt(column_a[..3].to_vec()),
433            OwnedColumn::VarChar(column_b[..3].to_vec()),
434        ];
435
436        let commitments_a = Vec::<NaiveCommitment>::from_columns_with_offset(&columns, 0, &());
437
438        let full_columns = vec![
439            OwnedColumn::<TestScalar>::BigInt(column_a.to_vec()),
440            OwnedColumn::VarChar(column_b.to_vec()),
441        ];
442
443        let commitments_b = Vec::<NaiveCommitment>::from_columns_with_offset(&full_columns, 0, &());
444
445        let commitments = commitments_b.try_sub(commitments_a).unwrap();
446
447        let committable_columns = [
448            CommittableColumn::BigInt(&column_a[3..]),
449            CommittableColumn::VarChar(
450                column_b[3..]
451                    .iter()
452                    .map(Into::<TestScalar>::into)
453                    .map(Into::<[u64; 4]>::into)
454                    .collect(),
455            ),
456        ];
457        let expected_commitments =
458            NaiveCommitment::compute_commitments(&committable_columns, 3, &());
459        assert_eq!(commitments, expected_commitments);
460    }
461
462    #[test]
463    fn we_cannot_sub_commitment_collections_of_mixed_column_counts() {
464        let column_a = [12i64, 34, 56, 78, 90];
465        let column_b = ["Lorem", "ipsum", "dolor", "sit", "amet"].map(String::from);
466
467        let columns = vec![
468            OwnedColumn::<TestScalar>::BigInt(column_a[..3].to_vec()),
469            OwnedColumn::VarChar(column_b[..3].to_vec()),
470        ];
471
472        let commitments = Vec::<NaiveCommitment>::from_columns_with_offset(&columns, 0, &());
473
474        let full_columns = Vec::<Column<TestScalar>>::new();
475        let full_commitments =
476            Vec::<NaiveCommitment>::from_columns_with_offset(&full_columns, 0, &());
477        assert!(matches!(
478            full_commitments.clone().try_sub(commitments.clone()),
479            Err(NumColumnsMismatch)
480        ));
481
482        let full_columns = vec![OwnedColumn::<TestScalar>::BigInt(column_a.to_vec())];
483        let full_commitments =
484            Vec::<NaiveCommitment>::from_columns_with_offset(&full_columns, 0, &());
485        assert!(matches!(
486            full_commitments.try_sub(commitments.clone()),
487            Err(NumColumnsMismatch)
488        ));
489
490        let full_columns = vec![
491            OwnedColumn::<TestScalar>::BigInt(column_a.to_vec()),
492            OwnedColumn::VarChar(column_b.to_vec()),
493            OwnedColumn::BigInt(column_a.to_vec()),
494        ];
495        let full_commitments =
496            Vec::<NaiveCommitment>::from_columns_with_offset(&full_columns, 0, &());
497        assert!(matches!(
498            full_commitments.try_sub(commitments),
499            Err(NumColumnsMismatch)
500        ));
501    }
502}