Skip to content

Commit 96974c5

Browse files
committed
feat(compact): implement CompactProtocol for composition protocols
- feat: add CompactProtocol trait implementation for OrProtocol and AndProtocol structures - feat: enable compact proof creation for composition protocols - test: add initial verification tests for compact composition proofs in tests/proof_composition
1 parent 6524e85 commit 96974c5

File tree

2 files changed

+189
-11
lines changed

2 files changed

+189
-11
lines changed

src/proof_composition.rs

Lines changed: 181 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ use ff::{Field, PrimeField};
22
use group::{Group, GroupEncoding};
33

44
use crate::{
5-
deserialize_scalar, serialize_scalar, ProofError, SchnorrProtocol, SigmaProtocol,
6-
SigmaProtocolSimulator,
5+
deserialize_scalar, serialize_scalar, CompactProtocol, ProofError, SchnorrProtocol,
6+
SigmaProtocol, SigmaProtocolSimulator,
77
};
88

99
#[derive(Default)]
@@ -139,19 +139,28 @@ impl<G: Group + GroupEncoding> SigmaProtocol for AndProtocol<G> {
139139
&self,
140140
data: &[u8],
141141
) -> Result<(Self::Commitment, Self::Response), ProofError> {
142-
let mut cursor = 0;
143-
let mut commitment = Vec::with_capacity(self.0.iter().map(|p| p.statements_nb()).sum());
144-
let mut response = Vec::with_capacity(self.0.iter().map(|p| p.scalars_nb()).sum());
145-
146142
let point_size = G::generator().to_bytes().as_ref().len();
147143
let scalar_size = <<G as Group>::Scalar as PrimeField>::Repr::default()
148144
.as_ref()
149145
.len();
150146

147+
let expected_d_len: usize = self
148+
.0
149+
.iter()
150+
.map(|p| p.scalars_nb() * scalar_size + p.statements_nb() * point_size)
151+
.sum();
152+
if data.len() != expected_d_len {
153+
return Err(ProofError::ProofSizeMismatch);
154+
}
155+
156+
let mut cursor = 0;
157+
let mut commitment = Vec::with_capacity(self.0.iter().map(|p| p.statements_nb()).sum());
158+
let mut response = Vec::with_capacity(self.0.iter().map(|p| p.scalars_nb()).sum());
159+
151160
for proto in &self.0 {
152-
let c_nb = proto.statements_nb();
153-
let r_nb = proto.scalars_nb();
154-
let proof_len = r_nb * scalar_size + c_nb * point_size;
161+
let c_len = proto.statements_nb();
162+
let r_len = proto.scalars_nb();
163+
let proof_len = r_len * scalar_size + c_len * point_size;
155164
let (proto_commit, proto_resp) =
156165
proto.deserialize_batchable(&data[cursor..(cursor + proof_len)])?;
157166
commitment.extend(proto_commit);
@@ -162,6 +171,78 @@ impl<G: Group + GroupEncoding> SigmaProtocol for AndProtocol<G> {
162171
}
163172
}
164173

174+
impl<G: Group + GroupEncoding> CompactProtocol for AndProtocol<G> {
175+
fn get_commitment(
176+
&self,
177+
challenge: &Self::Challenge,
178+
response: &Self::Response,
179+
) -> Result<Self::Commitment, ProofError> {
180+
let expected_r_len: usize = self.0.iter().map(|p| p.scalars_nb()).sum();
181+
if response.len() != expected_r_len {
182+
return Err(ProofError::Other);
183+
}
184+
185+
let mut commitment = Vec::with_capacity(self.0.iter().map(|p| p.statements_nb()).sum());
186+
let mut cursor = 0;
187+
for proto in &self.0 {
188+
let r_len = proto.scalars_nb();
189+
let proto_resp = response[cursor..(cursor + r_len)].to_vec();
190+
let proto_commit = proto.get_commitment(challenge, &proto_resp)?;
191+
commitment.extend(proto_commit);
192+
cursor += r_len;
193+
}
194+
Ok(commitment)
195+
}
196+
197+
fn serialize_compact(
198+
&self,
199+
commitment: &Self::Commitment,
200+
challenge: &Self::Challenge,
201+
response: &Self::Response,
202+
) -> Result<Vec<u8>, ProofError> {
203+
let expected_c_len: usize = self.0.iter().map(|p| p.statements_nb()).sum();
204+
let expected_r_len: usize = self.0.iter().map(|p| p.scalars_nb()).sum();
205+
if commitment.len() != expected_c_len || response.len() != expected_r_len {
206+
return Err(ProofError::Other);
207+
}
208+
209+
let mut bytes = Vec::new();
210+
bytes.extend(serialize_scalar::<G>(challenge));
211+
for resp in response {
212+
bytes.extend(serialize_scalar::<G>(resp));
213+
}
214+
Ok(bytes)
215+
}
216+
217+
fn deserialize_compact(
218+
&self,
219+
data: &[u8],
220+
) -> Result<(Self::Challenge, Self::Response), ProofError> {
221+
let scalar_size = <<G as Group>::Scalar as PrimeField>::Repr::default()
222+
.as_ref()
223+
.len();
224+
225+
let expected_d_len: usize = self.0.iter().map(|p| (p.scalars_nb()) * scalar_size).sum();
226+
if data.len() != expected_d_len + scalar_size {
227+
return Err(ProofError::ProofSizeMismatch);
228+
}
229+
230+
let challenge = deserialize_scalar::<G>(&data[..scalar_size])?;
231+
let mut cursor = scalar_size;
232+
let mut response = Vec::with_capacity(self.0.iter().map(|p| p.scalars_nb()).sum());
233+
for proto in &self.0 {
234+
let r_len = proto.scalars_nb();
235+
for _ in 0..r_len {
236+
response.push(deserialize_scalar::<G>(
237+
&data[cursor..(cursor + scalar_size)],
238+
)?);
239+
cursor += scalar_size;
240+
}
241+
}
242+
Ok((challenge, response))
243+
}
244+
}
245+
165246
#[derive(Default)]
166247
pub struct OrProtocol<G: Group + GroupEncoding>(pub Vec<SchnorrProtocol<G>>);
167248

@@ -374,3 +455,94 @@ impl<G: Group + GroupEncoding> SigmaProtocol for OrProtocol<G> {
374455
Ok((commitment, response))
375456
}
376457
}
458+
459+
impl<G: Group + GroupEncoding> CompactProtocol for OrProtocol<G> {
460+
fn get_commitment(
461+
&self,
462+
_challenge: &Self::Challenge,
463+
response: &Self::Response,
464+
) -> Result<Self::Commitment, ProofError> {
465+
let expected_ch_nb = self.len();
466+
let expected_r_len: usize = self.0.iter().map(|p| p.scalars_nb()).sum();
467+
if response.0.len() != expected_ch_nb || response.1.len() != expected_r_len {
468+
return Err(ProofError::Other);
469+
}
470+
471+
let mut commitment = Vec::with_capacity(self.0.iter().map(|p| p.statements_nb()).sum());
472+
let mut cursor = 0;
473+
for (i, proto) in self.0.iter().enumerate() {
474+
let r_len = proto.scalars_nb();
475+
let proto_resp = response.1[cursor..(cursor + r_len)].to_vec();
476+
let proto_commit = proto.get_commitment(&response.0[i], &proto_resp)?;
477+
commitment.extend(proto_commit);
478+
cursor += r_len;
479+
}
480+
Ok(commitment)
481+
}
482+
483+
fn serialize_compact(
484+
&self,
485+
commitment: &Self::Commitment,
486+
challenge: &Self::Challenge,
487+
response: &Self::Response,
488+
) -> Result<Vec<u8>, ProofError> {
489+
let expected_c_len: usize = self.0.iter().map(|p| p.statements_nb()).sum();
490+
let expected_ch_nb = self.len();
491+
let expected_r_len: usize = self.0.iter().map(|p| p.scalars_nb()).sum();
492+
if commitment.len() != expected_c_len
493+
|| response.0.len() != expected_ch_nb
494+
|| response.1.len() != expected_r_len
495+
{
496+
return Err(ProofError::Other);
497+
}
498+
499+
let mut bytes = Vec::new();
500+
bytes.extend(serialize_scalar::<G>(challenge));
501+
for i in 0..self.len() {
502+
bytes.extend(serialize_scalar::<G>(&response.0[i]));
503+
}
504+
for resp in &response.1 {
505+
bytes.extend(serialize_scalar::<G>(resp));
506+
}
507+
Ok(bytes)
508+
}
509+
510+
fn deserialize_compact(
511+
&self,
512+
data: &[u8],
513+
) -> Result<(Self::Challenge, Self::Response), ProofError> {
514+
let scalar_size = <<G as Group>::Scalar as PrimeField>::Repr::default()
515+
.as_ref()
516+
.len();
517+
518+
let expected_d_len: usize = self
519+
.0
520+
.iter()
521+
.map(|p| (p.scalars_nb() + 1) * scalar_size)
522+
.sum();
523+
if data.len() != expected_d_len + scalar_size {
524+
return Err(ProofError::ProofSizeMismatch);
525+
}
526+
527+
let challenge = deserialize_scalar::<G>(&data[..scalar_size])?;
528+
let mut cursor = scalar_size;
529+
let mut ch_resp = Vec::with_capacity(self.len());
530+
for _ in 0..self.len() {
531+
ch_resp.push(deserialize_scalar::<G>(
532+
&data[cursor..(cursor + scalar_size)],
533+
)?);
534+
cursor += scalar_size;
535+
}
536+
let mut response = Vec::with_capacity(self.0.iter().map(|p| p.scalars_nb()).sum());
537+
for proto in &self.0 {
538+
let r_len = proto.scalars_nb();
539+
for _ in 0..r_len {
540+
response.push(deserialize_scalar::<G>(
541+
&data[cursor..(cursor + scalar_size)],
542+
)?);
543+
cursor += scalar_size;
544+
}
545+
}
546+
Ok((challenge, (ch_resp, response)))
547+
}
548+
}

tests/proof_composition.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,10 +82,12 @@ fn and_proof_correct() {
8282

8383
// Batchable and compact proofs
8484
let proof_batchable_bytes = nizk.prove_batchable(&witness, &mut rng).unwrap();
85+
let proof_compact_bytes = nizk.prove_compact(&witness, &mut rng).unwrap();
8586
// Verify proofs
8687
let verified_batchable = nizk.verify_batchable(&proof_batchable_bytes).is_ok();
88+
let verified_compact = nizk.verify_compact(&proof_compact_bytes).is_ok();
8789
assert!(
88-
verified_batchable,
90+
verified_batchable & verified_compact,
8991
"Fiat-Shamir Schnorr proof verification failed"
9092
);
9193
}
@@ -147,10 +149,14 @@ fn or_proof_correct() {
147149

148150
// Batchable and compact proofs
149151
let proof_batchable_bytes = nizk.prove_batchable(&witness, &mut rng).unwrap();
152+
let proof_compact_bytes = nizk.prove_compact(&witness, &mut rng).unwrap();
153+
println!("batchable : {:?}", proof_batchable_bytes);
154+
println!("compact : {:?}", proof_compact_bytes);
150155
// Verify proofs
151156
let verified_batchable = nizk.verify_batchable(&proof_batchable_bytes).is_ok();
157+
let verified_compact = nizk.verify_compact(&proof_compact_bytes).is_ok();
152158
assert!(
153-
verified_batchable,
159+
verified_batchable & verified_compact,
154160
"Fiat-Shamir Schnorr proof verification failed"
155161
);
156162
}

0 commit comments

Comments
 (0)