use crate::pedersen::{PedersenCommitment, PedersenOpening, PedersenParams};
use curve25519_dalek::scalar::Scalar;
use rand::rngs::OsRng;
#[derive(Debug, Clone)]
pub struct BatchCommitment {
pub commitments: Vec<PedersenCommitment>,
pub openings: Vec<PedersenOpening>,
}
impl BatchCommitment {
pub fn commit_batch(params: &PedersenParams, values: &[Scalar]) -> Self {
let mut commitments = Vec::with_capacity(values.len());
let mut openings = Vec::with_capacity(values.len());
for value in values {
let (c, o) = PedersenCommitment::commit(params, value);
commitments.push(c);
openings.push(o);
}
BatchCommitment { commitments, openings }
}
pub fn commit_batch_u64(params: &PedersenParams, values: &[u64]) -> Self {
let scalars: Vec<Scalar> = values.iter().map(|&v| Scalar::from(v)).collect();
Self::commit_batch(params, &scalars)
}
pub fn verify_all(&self, params: &PedersenParams) -> bool {
if self.commitments.len() != self.openings.len() {
return false;
}
self.commitments
.iter()
.zip(&self.openings)
.all(|(c, o)| PedersenCommitment::verify(params, o, c))
}
pub fn len(&self) -> usize {
self.commitments.len()
}
pub fn is_empty(&self) -> bool {
self.commitments.is_empty()
}
pub fn aggregate(&self) -> (PedersenCommitment, PedersenOpening) {
assert!(!self.commitments.is_empty());
let mut agg_point = self.commitments[0].point;
let mut agg_value = self.openings[0].value;
let mut agg_randomness = self.openings[0].randomness;
for i in 1..self.commitments.len() {
let challenge = Scalar::random(&mut OsRng);
agg_point += challenge * self.commitments[i].point;
agg_value += challenge * self.openings[i].value;
agg_randomness += challenge * self.openings[i].randomness;
}
(
PedersenCommitment {
point: agg_point,
compressed: agg_point.compress(),
},
PedersenOpening {
value: agg_value,
randomness: agg_randomness,
},
)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn batch_commit_and_verify() {
let params = PedersenParams::default();
let batch = BatchCommitment::commit_batch_u64(¶ms, &[10, 20, 30, 40]);
assert!(batch.verify_all(¶ms));
assert_eq!(batch.len(), 4);
}
#[test]
fn batch_single_commitment() {
let params = PedersenParams::default();
let batch = BatchCommitment::commit_batch_u64(¶ms, &[42]);
assert!(batch.verify_all(¶ms));
assert_eq!(batch.len(), 1);
}
#[test]
fn batch_tampered_opening_fails() {
let params = PedersenParams::default();
let mut batch = BatchCommitment::commit_batch_u64(¶ms, &[10, 20, 30]);
batch.openings[1].value = Scalar::from(999u64);
assert!(!batch.verify_all(¶ms));
}
#[test]
fn batch_aggregate_verifies() {
let params = PedersenParams::default();
let batch = BatchCommitment::commit_batch_u64(¶ms, &[10, 20, 30]);
let (agg_comm, agg_open) = batch.aggregate();
assert!(PedersenCommitment::verify(¶ms, &agg_open, &agg_comm));
}
}