1use super::{DoryProverPublicSetup, GT};
26use crate::base::{
27 commitment::{Commitment, CommittableColumn},
28 impl_serde_for_ark_serde_checked,
29 scalar::MontScalar,
30};
31use alloc::vec::Vec;
32use ark_ec::pairing::PairingOutput;
33use ark_serialize::{CanonicalDeserialize, CanonicalSerialize};
34use core::ops::Mul;
35use derive_more::{AddAssign, Neg, Sub, SubAssign};
36use num_traits::One;
37
38pub type DoryScalar = MontScalar<ark_bls12_381::FrConfig>;
40
41#[derive(
42 Debug,
43 Sub,
44 Eq,
45 PartialEq,
46 Neg,
47 Copy,
48 Clone,
49 AddAssign,
50 SubAssign,
51 CanonicalSerialize,
52 CanonicalDeserialize,
53)]
54pub struct DoryCommitment(pub(super) GT);
56
57impl Default for DoryCommitment {
59 fn default() -> Self {
60 Self(PairingOutput(One::one()))
61 }
62}
63
64impl_serde_for_ark_serde_checked!(DoryCommitment);
66impl Mul<DoryCommitment> for DoryScalar {
67 type Output = DoryCommitment;
68 fn mul(self, rhs: DoryCommitment) -> Self::Output {
69 DoryCommitment(rhs.0 * self.0)
70 }
71}
72impl<'a> Mul<&'a DoryCommitment> for DoryScalar {
73 type Output = DoryCommitment;
74 fn mul(self, rhs: &'a DoryCommitment) -> Self::Output {
75 DoryCommitment(rhs.0 * self.0)
76 }
77}
78impl Commitment for DoryCommitment {
79 type Scalar = DoryScalar;
80 type PublicSetup<'a> = DoryProverPublicSetup<'a>;
81
82 fn compute_commitments(
83 committable_columns: &[CommittableColumn],
84 offset: usize,
85 setup: &Self::PublicSetup<'_>,
86 ) -> Vec<Self> {
87 super::compute_dory_commitments(committable_columns, offset, setup)
88 }
89
90 fn to_transcript_bytes(&self) -> Vec<u8> {
91 let mut buf = Vec::with_capacity(self.0.compressed_size());
92 self.0.serialize_compressed(&mut buf).unwrap();
93 buf
94 }
95}
96
97#[cfg(test)]
98mod tests {
99 use super::{DoryCommitment, DoryProverPublicSetup, DoryScalar, GT};
100 use crate::{
101 base::{
102 commitment::{Commitment, NumColumnsMismatch, VecCommitmentExt},
103 database::{Column, OwnedColumn},
104 scalar::test_scalar_constants,
105 },
106 proof_primitive::dory::{rand_util::test_rng, ProverSetup, PublicParameters},
107 };
108 use ark_ec::pairing::Pairing;
109 use ark_ff::UniformRand;
110 use rand::{rngs::StdRng, SeedableRng};
111
112 #[test]
113 fn we_have_correct_constants_for_dory_scalar() {
114 test_scalar_constants::<DoryScalar>();
115 }
116
117 #[test]
118 fn we_can_convert_from_columns() {
119 let public_parameters = PublicParameters::test_rand(5, &mut test_rng());
120 let prover_setup = ProverSetup::from(&public_parameters);
121 let setup = DoryProverPublicSetup::new(&prover_setup, 2);
122 let Gamma_1 = &public_parameters.Gamma_1;
123 let Gamma_2 = &public_parameters.Gamma_2;
124
125 let commitments = Vec::<DoryCommitment>::from_columns_with_offset(
127 Vec::<Column<DoryScalar>>::new(),
128 0,
129 &setup,
130 );
131
132 assert!(commitments.is_empty());
133
134 let column_a = [12i64, 34, 56];
136 let column_b = ["Lorem", "ipsum", "dolor"].map(String::from);
137
138 let columns = vec![
139 OwnedColumn::<DoryScalar>::BigInt(column_a.to_vec()),
140 OwnedColumn::VarChar(column_b.to_vec()),
141 ];
142
143 let commitments = Vec::<DoryCommitment>::from_columns_with_offset(&columns, 0, &setup);
144
145 let mut expected_commitments = vec![DoryCommitment::default(); 2];
146 expected_commitments[0] = DoryCommitment(
147 Pairing::pairing(Gamma_1[0], Gamma_2[0]) * DoryScalar::from(column_a[0]).0
148 + Pairing::pairing(Gamma_1[1], Gamma_2[0]) * DoryScalar::from(column_a[1]).0
149 + Pairing::pairing(Gamma_1[2], Gamma_2[0]) * DoryScalar::from(column_a[2]).0,
150 );
151 expected_commitments[1] = DoryCommitment(
152 Pairing::pairing(Gamma_1[0], Gamma_2[0]) * DoryScalar::from(column_b[0].clone()).0
153 + Pairing::pairing(Gamma_1[1], Gamma_2[0])
154 * DoryScalar::from(column_b[1].clone()).0
155 + Pairing::pairing(Gamma_1[2], Gamma_2[0])
156 * DoryScalar::from(column_b[2].clone()).0,
157 );
158
159 assert_eq!(commitments, expected_commitments);
160 }
161
162 #[test]
163 fn we_can_append_rows() {
164 let public_parameters = PublicParameters::test_rand(5, &mut test_rng());
165 let prover_setup = ProverSetup::from(&public_parameters);
166 let setup = DoryProverPublicSetup::new(&prover_setup, 2);
167 let Gamma_1 = &public_parameters.Gamma_1;
168 let Gamma_2 = &public_parameters.Gamma_2;
169
170 let column_a = [12i64, 34, 56, 78, 90];
171 let column_b = ["Lorem", "ipsum", "dolor", "sit", "amet"].map(String::from);
172
173 let columns = vec![
174 OwnedColumn::<DoryScalar>::BigInt(column_a[..3].to_vec()),
175 OwnedColumn::VarChar(column_b[..3].to_vec()),
176 ];
177
178 let mut commitments = Vec::<DoryCommitment>::from_columns_with_offset(&columns, 0, &setup);
179
180 let new_columns = vec![
181 OwnedColumn::<DoryScalar>::BigInt(column_a[3..].to_vec()),
182 OwnedColumn::VarChar(column_b[3..].to_vec()),
183 ];
184
185 commitments
186 .try_append_rows_with_offset(&new_columns, 3, &setup)
187 .unwrap();
188
189 let mut expected_commitments = vec![DoryCommitment::default(); 2];
190 expected_commitments[0] = DoryCommitment(
191 Pairing::pairing(Gamma_1[0], Gamma_2[0]) * DoryScalar::from(column_a[0]).0
192 + Pairing::pairing(Gamma_1[1], Gamma_2[0]) * DoryScalar::from(column_a[1]).0
193 + Pairing::pairing(Gamma_1[2], Gamma_2[0]) * DoryScalar::from(column_a[2]).0
194 + Pairing::pairing(Gamma_1[3], Gamma_2[0]) * DoryScalar::from(column_a[3]).0
195 + Pairing::pairing(Gamma_1[0], Gamma_2[1]) * DoryScalar::from(column_a[4]).0,
196 );
197 expected_commitments[1] = DoryCommitment(
198 Pairing::pairing(Gamma_1[0], Gamma_2[0]) * DoryScalar::from(column_b[0].clone()).0
199 + Pairing::pairing(Gamma_1[1], Gamma_2[0])
200 * DoryScalar::from(column_b[1].clone()).0
201 + Pairing::pairing(Gamma_1[2], Gamma_2[0])
202 * DoryScalar::from(column_b[2].clone()).0
203 + Pairing::pairing(Gamma_1[3], Gamma_2[0])
204 * DoryScalar::from(column_b[3].clone()).0
205 + Pairing::pairing(Gamma_1[0], Gamma_2[1])
206 * DoryScalar::from(column_b[4].clone()).0,
207 );
208
209 assert_eq!(commitments, expected_commitments);
210 }
211
212 #[test]
213 fn we_cannot_append_rows_with_different_column_count() {
214 let public_parameters = PublicParameters::test_rand(5, &mut test_rng());
215 let prover_setup = ProverSetup::from(&public_parameters);
216 let setup = DoryProverPublicSetup::new(&prover_setup, 2);
217
218 let column_a = [12i64, 34, 56, 78, 90];
219 let column_b = ["Lorem", "ipsum", "dolor", "sit", "amet"].map(String::from);
220
221 let columns = vec![
222 OwnedColumn::<DoryScalar>::BigInt(column_a[..3].to_vec()),
223 OwnedColumn::VarChar(column_b[..3].to_vec()),
224 ];
225
226 let mut commitments = Vec::<DoryCommitment>::from_columns_with_offset(&columns, 0, &setup);
227
228 let new_columns = Vec::<Column<DoryScalar>>::new();
229 assert!(matches!(
230 commitments.try_append_rows_with_offset(&new_columns, 3, &setup),
231 Err(NumColumnsMismatch)
232 ));
233
234 let new_columns = vec![OwnedColumn::<DoryScalar>::BigInt(column_a[3..].to_vec())];
235 assert!(matches!(
236 commitments.try_append_rows_with_offset(&new_columns, 3, &setup),
237 Err(NumColumnsMismatch)
238 ));
239
240 let new_columns = vec![
241 OwnedColumn::<DoryScalar>::BigInt(column_a[3..].to_vec()),
242 OwnedColumn::VarChar(column_b[3..].to_vec()),
243 OwnedColumn::BigInt(column_a[3..].to_vec()),
244 ];
245 assert!(matches!(
246 commitments.try_append_rows_with_offset(&new_columns, 3, &setup),
247 Err(NumColumnsMismatch)
248 ));
249 }
250
251 #[test]
252 fn we_can_extend_columns() {
253 let public_parameters = PublicParameters::test_rand(5, &mut test_rng());
254 let prover_setup = ProverSetup::from(&public_parameters);
255 let setup = DoryProverPublicSetup::new(&prover_setup, 2);
256 let Gamma_1 = &public_parameters.Gamma_1;
257 let Gamma_2 = &public_parameters.Gamma_2;
258
259 let column_a = [12i64, 34, 56];
260 let column_b = ["Lorem", "ipsum", "dolor"].map(String::from);
261 let column_c = ["sit", "amet", "consectetur"].map(String::from);
262 let column_d = [78i64, 90, 1112];
263
264 let columns = vec![
265 OwnedColumn::<DoryScalar>::BigInt(column_a.to_vec()),
266 OwnedColumn::VarChar(column_b.to_vec()),
267 ];
268
269 let mut commitments = Vec::<DoryCommitment>::from_columns_with_offset(&columns, 0, &setup);
270
271 let new_columns = vec![
272 OwnedColumn::<DoryScalar>::VarChar(column_c.to_vec()),
273 OwnedColumn::BigInt(column_d.to_vec()),
274 ];
275
276 commitments.extend_columns_with_offset(&new_columns, 0, &setup);
277
278 let mut expected_commitments = vec![DoryCommitment::default(); 4];
279
280 expected_commitments[0] = DoryCommitment(
281 Pairing::pairing(Gamma_1[0], Gamma_2[0]) * DoryScalar::from(column_a[0]).0
282 + Pairing::pairing(Gamma_1[1], Gamma_2[0]) * DoryScalar::from(column_a[1]).0
283 + Pairing::pairing(Gamma_1[2], Gamma_2[0]) * DoryScalar::from(column_a[2]).0,
284 );
285 expected_commitments[1] = DoryCommitment(
286 Pairing::pairing(Gamma_1[0], Gamma_2[0]) * DoryScalar::from(column_b[0].clone()).0
287 + Pairing::pairing(Gamma_1[1], Gamma_2[0])
288 * DoryScalar::from(column_b[1].clone()).0
289 + Pairing::pairing(Gamma_1[2], Gamma_2[0])
290 * DoryScalar::from(column_b[2].clone()).0,
291 );
292 expected_commitments[2] = DoryCommitment(
293 Pairing::pairing(Gamma_1[0], Gamma_2[0]) * DoryScalar::from(column_c[0].clone()).0
294 + Pairing::pairing(Gamma_1[1], Gamma_2[0])
295 * DoryScalar::from(column_c[1].clone()).0
296 + Pairing::pairing(Gamma_1[2], Gamma_2[0])
297 * DoryScalar::from(column_c[2].clone()).0,
298 );
299 expected_commitments[3] = DoryCommitment(
300 Pairing::pairing(Gamma_1[0], Gamma_2[0]) * DoryScalar::from(column_d[0]).0
301 + Pairing::pairing(Gamma_1[1], Gamma_2[0]) * DoryScalar::from(column_d[1]).0
302 + Pairing::pairing(Gamma_1[2], Gamma_2[0]) * DoryScalar::from(column_d[2]).0,
303 );
304
305 assert_eq!(commitments, expected_commitments);
306 }
307
308 #[test]
309 fn we_can_add_commitment_collections() {
310 let public_parameters = PublicParameters::test_rand(5, &mut test_rng());
311 let prover_setup = ProverSetup::from(&public_parameters);
312 let setup = DoryProverPublicSetup::new(&prover_setup, 2);
313 let Gamma_1 = &public_parameters.Gamma_1;
314 let Gamma_2 = &public_parameters.Gamma_2;
315
316 let column_a = [12i64, 34, 56, 78, 90];
317 let column_b = ["Lorem", "ipsum", "dolor", "sit", "amet"].map(String::from);
318
319 let columns = vec![
320 OwnedColumn::<DoryScalar>::BigInt(column_a[..3].to_vec()),
321 OwnedColumn::VarChar(column_b[..3].to_vec()),
322 ];
323
324 let commitments_a = Vec::<DoryCommitment>::from_columns_with_offset(&columns, 0, &setup);
325
326 let new_columns = vec![
327 OwnedColumn::<DoryScalar>::BigInt(column_a[3..].to_vec()),
328 OwnedColumn::VarChar(column_b[3..].to_vec()),
329 ];
330
331 let commitments_b =
332 Vec::<DoryCommitment>::from_columns_with_offset(&new_columns, 3, &setup);
333
334 let commitments = commitments_a.try_add(commitments_b).unwrap();
335
336 let mut expected_commitments = vec![DoryCommitment::default(); 2];
337 expected_commitments[0] = DoryCommitment(
338 Pairing::pairing(Gamma_1[0], Gamma_2[0]) * DoryScalar::from(column_a[0]).0
339 + Pairing::pairing(Gamma_1[1], Gamma_2[0]) * DoryScalar::from(column_a[1]).0
340 + Pairing::pairing(Gamma_1[2], Gamma_2[0]) * DoryScalar::from(column_a[2]).0
341 + Pairing::pairing(Gamma_1[3], Gamma_2[0]) * DoryScalar::from(column_a[3]).0
342 + Pairing::pairing(Gamma_1[0], Gamma_2[1]) * DoryScalar::from(column_a[4]).0,
343 );
344 expected_commitments[1] = DoryCommitment(
345 Pairing::pairing(Gamma_1[0], Gamma_2[0]) * DoryScalar::from(column_b[0].clone()).0
346 + Pairing::pairing(Gamma_1[1], Gamma_2[0])
347 * DoryScalar::from(column_b[1].clone()).0
348 + Pairing::pairing(Gamma_1[2], Gamma_2[0])
349 * DoryScalar::from(column_b[2].clone()).0
350 + Pairing::pairing(Gamma_1[3], Gamma_2[0])
351 * DoryScalar::from(column_b[3].clone()).0
352 + Pairing::pairing(Gamma_1[0], Gamma_2[1])
353 * DoryScalar::from(column_b[4].clone()).0,
354 );
355
356 assert_eq!(commitments, expected_commitments);
357 }
358
359 #[test]
360 fn we_cannot_add_commitment_collections_of_mixed_column_counts() {
361 let public_parameters = PublicParameters::test_rand(5, &mut test_rng());
362 let prover_setup = ProverSetup::from(&public_parameters);
363 let setup = DoryProverPublicSetup::new(&prover_setup, 2);
364
365 let column_a = [12i64, 34, 56, 78, 90];
366 let column_b = ["Lorem", "ipsum", "dolor", "sit", "amet"].map(String::from);
367
368 let columns = vec![
369 OwnedColumn::<DoryScalar>::BigInt(column_a[..3].to_vec()),
370 OwnedColumn::VarChar(column_b[..3].to_vec()),
371 ];
372
373 let commitments = Vec::<DoryCommitment>::from_columns_with_offset(&columns, 0, &setup);
374
375 let new_columns = Vec::<Column<DoryScalar>>::new();
376 let new_commitments =
377 Vec::<DoryCommitment>::from_columns_with_offset(&new_columns, 3, &setup);
378 assert!(matches!(
379 commitments.clone().try_add(new_commitments),
380 Err(NumColumnsMismatch)
381 ));
382
383 let new_columns = vec![OwnedColumn::<DoryScalar>::BigInt(column_a[3..].to_vec())];
384 let new_commitments =
385 Vec::<DoryCommitment>::from_columns_with_offset(&new_columns, 3, &setup);
386 assert!(matches!(
387 commitments.clone().try_add(new_commitments),
388 Err(NumColumnsMismatch)
389 ));
390
391 let new_columns = vec![
392 OwnedColumn::<DoryScalar>::BigInt(column_a[3..].to_vec()),
393 OwnedColumn::VarChar(column_b[3..].to_vec()),
394 OwnedColumn::BigInt(column_a[3..].to_vec()),
395 ];
396 let new_commitments =
397 Vec::<DoryCommitment>::from_columns_with_offset(&new_columns, 3, &setup);
398 assert!(matches!(
399 commitments.try_add(new_commitments),
400 Err(NumColumnsMismatch)
401 ));
402 }
403
404 #[test]
405 fn we_can_sub_commitment_collections() {
406 let public_parameters = PublicParameters::test_rand(5, &mut test_rng());
407 let prover_setup = ProverSetup::from(&public_parameters);
408 let setup = DoryProverPublicSetup::new(&prover_setup, 2);
409 let Gamma_1 = &public_parameters.Gamma_1;
410 let Gamma_2 = &public_parameters.Gamma_2;
411
412 let column_a = [12i64, 34, 56, 78, 90];
413 let column_b = ["Lorem", "ipsum", "dolor", "sit", "amet"].map(String::from);
414
415 let columns = vec![
416 OwnedColumn::<DoryScalar>::BigInt(column_a[..3].to_vec()),
417 OwnedColumn::VarChar(column_b[..3].to_vec()),
418 ];
419
420 let commitments_a = Vec::<DoryCommitment>::from_columns_with_offset(&columns, 0, &setup);
421
422 let full_columns = vec![
423 OwnedColumn::<DoryScalar>::BigInt(column_a.to_vec()),
424 OwnedColumn::VarChar(column_b.to_vec()),
425 ];
426
427 let commitments_b =
428 Vec::<DoryCommitment>::from_columns_with_offset(&full_columns, 0, &setup);
429
430 let commitments = commitments_b.try_sub(commitments_a).unwrap();
431
432 let mut expected_commitments = vec![DoryCommitment::default(); 2];
433
434 expected_commitments[0] = DoryCommitment(
435 Pairing::pairing(Gamma_1[3], Gamma_2[0]) * DoryScalar::from(column_a[3]).0
436 + Pairing::pairing(Gamma_1[0], Gamma_2[1]) * DoryScalar::from(column_a[4]).0,
437 );
438 expected_commitments[1] = DoryCommitment(
439 Pairing::pairing(Gamma_1[3], Gamma_2[0]) * DoryScalar::from(column_b[3].clone()).0
440 + Pairing::pairing(Gamma_1[0], Gamma_2[1])
441 * DoryScalar::from(column_b[4].clone()).0,
442 );
443
444 assert_eq!(commitments, expected_commitments);
445 }
446
447 #[test]
448 fn we_cannot_sub_commitment_collections_of_mixed_column_counts() {
449 let public_parameters = PublicParameters::test_rand(5, &mut test_rng());
450 let prover_setup = ProverSetup::from(&public_parameters);
451 let setup = DoryProverPublicSetup::new(&prover_setup, 2);
452
453 let column_a = [12i64, 34, 56, 78, 90];
454 let column_b = ["Lorem", "ipsum", "dolor", "sit", "amet"].map(String::from);
455
456 let columns = vec![
457 OwnedColumn::<DoryScalar>::BigInt(column_a[..3].to_vec()),
458 OwnedColumn::VarChar(column_b[..3].to_vec()),
459 ];
460
461 let commitments = Vec::<DoryCommitment>::from_columns_with_offset(&columns, 0, &setup);
462
463 let full_columns = Vec::<Column<DoryScalar>>::new();
464 let full_commitments =
465 Vec::<DoryCommitment>::from_columns_with_offset(&full_columns, 0, &setup);
466 assert!(matches!(
467 full_commitments.clone().try_sub(commitments.clone()),
468 Err(NumColumnsMismatch)
469 ));
470
471 let full_columns = vec![OwnedColumn::<DoryScalar>::BigInt(column_a.to_vec())];
472 let full_commitments =
473 Vec::<DoryCommitment>::from_columns_with_offset(&full_columns, 0, &setup);
474 assert!(matches!(
475 full_commitments.try_sub(commitments.clone()),
476 Err(NumColumnsMismatch)
477 ));
478
479 let full_columns = vec![
480 OwnedColumn::<DoryScalar>::BigInt(column_a.to_vec()),
481 OwnedColumn::VarChar(column_b.to_vec()),
482 OwnedColumn::BigInt(column_a.to_vec()),
483 ];
484 let full_commitments =
485 Vec::<DoryCommitment>::from_columns_with_offset(&full_columns, 0, &setup);
486 assert!(matches!(
487 full_commitments.try_sub(commitments),
488 Err(NumColumnsMismatch)
489 ));
490 }
491
492 #[test]
493 fn we_get_different_transcript_bytes_from_different_dory_commitments() {
494 let mut rng = StdRng::seed_from_u64(42);
495 let commitment1 = DoryCommitment(GT::rand(&mut rng));
496 let commitment2 = DoryCommitment(GT::rand(&mut rng));
497 assert_ne!(
498 commitment1.to_transcript_bytes(),
499 commitment2.to_transcript_bytes()
500 );
501 }
502}