si_commitment_scheme/
batch.rs1use crate::pedersen::{PedersenCommitment, PedersenOpening, PedersenParams};
6use curve25519_dalek::scalar::Scalar;
7use rand::rngs::OsRng;
8
9#[derive(Debug, Clone)]
11pub struct BatchCommitment {
12 pub commitments: Vec<PedersenCommitment>,
14 pub openings: Vec<PedersenOpening>,
16}
17
18impl BatchCommitment {
19 pub fn commit_batch(params: &PedersenParams, values: &[Scalar]) -> Self {
21 let mut commitments = Vec::with_capacity(values.len());
22 let mut openings = Vec::with_capacity(values.len());
23 for value in values {
24 let (c, o) = PedersenCommitment::commit(params, value);
25 commitments.push(c);
26 openings.push(o);
27 }
28 BatchCommitment { commitments, openings }
29 }
30
31 pub fn commit_batch_u64(params: &PedersenParams, values: &[u64]) -> Self {
33 let scalars: Vec<Scalar> = values.iter().map(|&v| Scalar::from(v)).collect();
34 Self::commit_batch(params, &scalars)
35 }
36
37 pub fn verify_all(&self, params: &PedersenParams) -> bool {
39 if self.commitments.len() != self.openings.len() {
40 return false;
41 }
42 self.commitments
43 .iter()
44 .zip(&self.openings)
45 .all(|(c, o)| PedersenCommitment::verify(params, o, c))
46 }
47
48 pub fn len(&self) -> usize {
50 self.commitments.len()
51 }
52
53 pub fn is_empty(&self) -> bool {
55 self.commitments.is_empty()
56 }
57
58 pub fn aggregate(&self) -> (PedersenCommitment, PedersenOpening) {
61 assert!(!self.commitments.is_empty());
62 let mut agg_point = self.commitments[0].point;
63 let mut agg_value = self.openings[0].value;
64 let mut agg_randomness = self.openings[0].randomness;
65
66 for i in 1..self.commitments.len() {
67 let challenge = Scalar::random(&mut OsRng);
68 agg_point += challenge * self.commitments[i].point;
69 agg_value += challenge * self.openings[i].value;
70 agg_randomness += challenge * self.openings[i].randomness;
71 }
72
73 (
74 PedersenCommitment {
75 point: agg_point,
76 compressed: agg_point.compress(),
77 },
78 PedersenOpening {
79 value: agg_value,
80 randomness: agg_randomness,
81 },
82 )
83 }
84}
85
86#[cfg(test)]
87mod tests {
88 use super::*;
89
90 #[test]
91 fn batch_commit_and_verify() {
92 let params = PedersenParams::default();
93 let batch = BatchCommitment::commit_batch_u64(¶ms, &[10, 20, 30, 40]);
94 assert!(batch.verify_all(¶ms));
95 assert_eq!(batch.len(), 4);
96 }
97
98 #[test]
99 fn batch_single_commitment() {
100 let params = PedersenParams::default();
101 let batch = BatchCommitment::commit_batch_u64(¶ms, &[42]);
102 assert!(batch.verify_all(¶ms));
103 assert_eq!(batch.len(), 1);
104 }
105
106 #[test]
107 fn batch_tampered_opening_fails() {
108 let params = PedersenParams::default();
109 let mut batch = BatchCommitment::commit_batch_u64(¶ms, &[10, 20, 30]);
110 batch.openings[1].value = Scalar::from(999u64);
111 assert!(!batch.verify_all(¶ms));
112 }
113
114 #[test]
115 fn batch_aggregate_verifies() {
116 let params = PedersenParams::default();
117 let batch = BatchCommitment::commit_batch_u64(¶ms, &[10, 20, 30]);
118 let (agg_comm, agg_open) = batch.aggregate();
119 assert!(PedersenCommitment::verify(¶ms, &agg_open, &agg_comm));
120 }
121}