1use super::Commitment;
2use crate::base::commitment::committable_column::CommittableColumn;
3use alloc::vec::Vec;
4use snafu::Snafu;
5
6#[derive(Snafu, Debug)]
8#[snafu(display("cannot update commitment collections with different column counts"))]
9pub struct NumColumnsMismatch;
10
11pub trait VecCommitmentExt {
15 type CommitmentPublicSetup<'a>;
18
19 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 fn from_committable_columns_with_offset(
30 committable_columns: &[CommittableColumn],
31 offset: usize,
32 setup: &Self::CommitmentPublicSetup<'_>,
33 ) -> Self;
34
35 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 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 fn try_add(self, other: Self) -> Result<Self, NumColumnsMismatch>
61 where
62 Self: Sized;
63
64 fn try_sub(self, other: Self) -> Result<Self, NumColumnsMismatch>
66 where
67 Self: Sized;
68
69 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 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 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}