Skip to content

Commit 02e495f

Browse files
committed
move validation criterias for relation in CanonicalLinearRelation
1 parent e46ed48 commit 02e495f

File tree

9 files changed

+278
-206
lines changed

9 files changed

+278
-206
lines changed

src/composition.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,10 @@ where
6060
G: PrimeGroup,
6161
{
6262
fn from(value: LinearRelation<G>) -> Self {
63-
Self::from(SchnorrProof::from(value))
63+
Self::Simple(
64+
SchnorrProof::try_from(value)
65+
.expect("Failed to convert LinearRelation to SchnorrProof"),
66+
)
6467
}
6568
}
6669

src/linear_relation/mod.rs

Lines changed: 30 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -435,7 +435,7 @@ impl<G: PrimeGroup> CanonicalLinearRelation<G> {
435435
Ok(())
436436
}
437437

438-
/// Serialize the canonical linear relation to bytes.
438+
/// Serialize the linear relation to bytes.
439439
///
440440
/// The output format is:
441441
/// - [Ne: u32] number of equations
@@ -444,7 +444,7 @@ impl<G: PrimeGroup> CanonicalLinearRelation<G> {
444444
/// - [Nt: u32] number of terms
445445
/// - Nt × [scalar_index: u32, group_index: u32] term entries
446446
/// - Followed by all group elements in serialized form
447-
pub fn label(&self) -> Result<Vec<u8>, Error> {
447+
pub fn label(&self) -> Vec<u8> {
448448
let mut out = Vec::new();
449449

450450
// Replicate the original LinearRelationReprBuilder ordering behavior
@@ -473,7 +473,10 @@ impl<G: PrimeGroup> CanonicalLinearRelation<G> {
473473
// Build the RHS terms
474474
let mut rhs_terms = Vec::new();
475475
for (scalar_var, group_var) in constraint_terms {
476-
let group_elem = self.group_elements.get(*group_var)?;
476+
let group_elem = self
477+
.group_elements
478+
.get(*group_var)
479+
.expect("Group element not found");
477480
let group_index = repr_index(group_elem.to_bytes());
478481
rhs_terms.push((scalar_var.0 as u32, group_index));
479482
}
@@ -505,19 +508,32 @@ impl<G: PrimeGroup> CanonicalLinearRelation<G> {
505508
out.extend_from_slice(elem_repr.as_ref());
506509
}
507510

508-
Ok(out)
511+
out
509512
}
510513
}
511514

512-
impl<G: PrimeGroup> TryFrom<LinearRelation<G>> for CanonicalLinearRelation<G> {
515+
impl<G: PrimeGroup> TryFrom<&LinearRelation<G>> for CanonicalLinearRelation<G> {
513516
type Error = Error;
514517

515-
fn try_from(relation: LinearRelation<G>) -> Result<Self, Self::Error> {
516-
assert_eq!(
517-
relation.image.len(),
518-
relation.linear_map.linear_combinations.len(),
519-
"Number of equations and image variables must match"
520-
);
518+
fn try_from(relation: &LinearRelation<G>) -> Result<Self, Self::Error> {
519+
// Number of equations and image variables must match
520+
if relation.image.len() != relation.linear_map.linear_combinations.len() {
521+
return Err(Error::InvalidInstanceWitnessPair);
522+
}
523+
524+
// If the image is the identity, then the relation must be trivial, or else the proof will be unsound
525+
if !relation.image().is_ok_and(|img| img.iter().all(|&x| x != G::identity())) {
526+
return Err(Error::InvalidInstanceWitnessPair);
527+
}
528+
529+
// Empty relations (without constraints) cannot be proven
530+
if relation.linear_map.linear_combinations.is_empty() {
531+
return Err(Error::InvalidInstanceWitnessPair);
532+
}
533+
534+
if relation.linear_map.linear_combinations.iter().any(|lc| lc.0.is_empty()) {
535+
return Err(Error::InvalidInstanceWitnessPair);
536+
}
521537

522538
let mut canonical = CanonicalLinearRelation::new();
523539
canonical.num_scalars = relation.linear_map.num_scalars;
@@ -532,7 +548,7 @@ impl<G: PrimeGroup> TryFrom<LinearRelation<G>> for CanonicalLinearRelation<G> {
532548
canonical.process_constraint(
533549
*image_var,
534550
equation,
535-
&relation,
551+
relation,
536552
&mut weighted_group_cache,
537553
)?;
538554
}
@@ -709,20 +725,6 @@ impl<G: PrimeGroup> LinearRelation<G> {
709725
.collect()
710726
}
711727

712-
/// Returns a binary label describing the linear map.
713-
///
714-
/// The format is:
715-
/// - [Ne: u32] number of equations
716-
/// - For each equation:
717-
/// - [output_point_index: u32]
718-
/// - [Nt: u32] number of terms
719-
/// - Nt × [scalar_index: u32, point_index: u32] term entries
720-
pub fn label(&self) -> Vec<u8> {
721-
// XXX. We should return an error if the group elements are not assigned, instead of panicking.
722-
let canonical: CanonicalLinearRelation<G> = self.clone().try_into().unwrap();
723-
canonical.label().unwrap()
724-
}
725-
726728
/// Convert this LinearRelation into a non-interactive zero-knowledge protocol
727729
/// using the ShakeCodec and a specified context/domain separator.
728730
///
@@ -758,7 +760,8 @@ impl<G: PrimeGroup> LinearRelation<G> {
758760
self,
759761
session_identifier: &[u8],
760762
) -> Nizk<SchnorrProof<G>, Shake128DuplexSponge<G>> {
761-
let schnorr = SchnorrProof::from(self);
763+
let schnorr =
764+
SchnorrProof::try_from(self).expect("Failed to convert LinearRelation to SchnorrProof");
762765
Nizk::new(session_identifier, schnorr)
763766
}
764767
}

src/schnorr_protocol.rs

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -54,13 +54,12 @@ impl<G: PrimeGroup> SchnorrProof<G> {
5454
}
5555
}
5656

57-
impl<G: PrimeGroup> From<LinearRelation<G>> for SchnorrProof<G> {
58-
fn from(value: LinearRelation<G>) -> Self {
59-
Self(
60-
value
61-
.try_into()
62-
.expect("Failed to convert LinearRelation to CanonicalLinearRelation"),
63-
)
57+
impl<G: PrimeGroup> TryFrom<LinearRelation<G>> for SchnorrProof<G> {
58+
type Error = Error;
59+
60+
fn try_from(linear_relation: LinearRelation<G>) -> Result<Self, Self::Error> {
61+
let canonical_linear_relation = (&linear_relation).try_into()?;
62+
Ok(Self(canonical_linear_relation))
6463
}
6564
}
6665

@@ -108,9 +107,9 @@ where
108107
return Err(Error::InvalidInstanceWitnessPair);
109108
}
110109

111-
let nonces: Vec<G::Scalar> = (0..self.witness_length())
110+
let nonces = (0..self.witness_length())
112111
.map(|_| G::Scalar::random(&mut *rng))
113-
.collect();
112+
.collect::<Vec<_>>();
114113
let commitment = self.evaluate(&nonces)?;
115114
let prover_state = (nonces, witness.clone());
116115
Ok((commitment, prover_state))
@@ -280,7 +279,7 @@ where
280279
}
281280

282281
fn instance_label(&self) -> impl AsRef<[u8]> {
283-
self.0.label().unwrap_or_default()
282+
self.0.label()
284283
}
285284

286285
fn protocol_identifier(&self) -> impl AsRef<[u8]> {

src/tests/composition.rs

Lines changed: 12 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
use curve25519_dalek::ristretto::RistrettoPoint;
2-
use group::Group;
32
use rand::rngs::OsRng;
43

54
use super::test_utils::{
@@ -26,55 +25,25 @@ fn composition_proof_correct() {
2625
let domain_sep = b"hello world";
2726

2827
// definitions of the underlying protocols
29-
let (relation1, witness1) = dleq(
30-
G::random(&mut OsRng),
31-
<G as Group>::Scalar::random(&mut OsRng),
32-
);
33-
let (relation2, _) = pedersen_commitment(
34-
G::random(&mut OsRng),
35-
<G as Group>::Scalar::random(&mut OsRng),
36-
<G as Group>::Scalar::random(&mut OsRng),
37-
);
38-
let (relation3, witness3) = discrete_logarithm(<G as Group>::Scalar::random(&mut OsRng));
39-
let (relation4, witness4) = pedersen_commitment_dleq(
40-
(0..4)
41-
.map(|_| G::random(&mut OsRng))
42-
.collect::<Vec<_>>()
43-
.try_into()
44-
.unwrap(),
45-
(0..2)
46-
.map(|_| <G as Group>::Scalar::random(&mut OsRng))
47-
.collect::<Vec<_>>()
48-
.try_into()
49-
.unwrap(),
50-
);
51-
let (relation5, witness5) = bbs_blind_commitment_computation(
52-
(0..4)
53-
.map(|_| G::random(&mut OsRng))
54-
.collect::<Vec<_>>()
55-
.try_into()
56-
.unwrap(),
57-
(0..3)
58-
.map(|_| <G as Group>::Scalar::random(&mut OsRng))
59-
.collect::<Vec<_>>()
60-
.try_into()
61-
.unwrap(),
62-
<G as Group>::Scalar::random(&mut OsRng),
63-
);
28+
let (relation1, witness1) = dleq::<G>();
29+
let (relation2, _) = pedersen_commitment::<G>();
30+
let (relation3, witness3) = discrete_logarithm::<G>();
31+
let (relation4, witness4) = pedersen_commitment_dleq::<G>();
32+
let (relation5, witness5) = bbs_blind_commitment_computation::<G>();
6433

6534
// second layer protocol definitions
6635
let or_protocol1 = Protocol::Or(vec![
67-
Protocol::Simple(SchnorrProof::from(relation1)),
68-
Protocol::Simple(SchnorrProof::from(relation2)),
36+
Protocol::Simple(SchnorrProof(relation1)),
37+
Protocol::Simple(SchnorrProof(relation2)),
6938
]);
7039
let or_witness1 = ProtocolWitness::Or(0, vec![ProtocolWitness::Simple(witness1)]);
7140

72-
let simple_protocol1 = Protocol::from(relation3);
41+
let simple_protocol1 = Protocol::Simple(SchnorrProof(relation3));
7342
let simple_witness1 = ProtocolWitness::Simple(witness3);
7443

7544
let and_protocol1 = Protocol::And(vec![
76-
Protocol::Simple(SchnorrProof::from(relation4)),
77-
Protocol::Simple(SchnorrProof::from(relation5)),
45+
Protocol::Simple(SchnorrProof(relation4)),
46+
Protocol::Simple(SchnorrProof(relation5)),
7847
]);
7948
let and_witness1 = ProtocolWitness::And(vec![
8049
ProtocolWitness::Simple(witness4),
@@ -91,10 +60,6 @@ fn composition_proof_correct() {
9160
let proof_batchable_bytes = nizk.prove_batchable(&witness, &mut OsRng).unwrap();
9261
let proof_compact_bytes = nizk.prove_compact(&witness, &mut OsRng).unwrap();
9362
// Verify proofs
94-
let verified_batchable = nizk.verify_batchable(&proof_batchable_bytes).is_ok();
95-
let verified_compact = nizk.verify_compact(&proof_compact_bytes).is_ok();
96-
assert!(
97-
verified_batchable & verified_compact,
98-
"Fiat-Shamir Schnorr proof verification failed"
99-
);
63+
assert!(nizk.verify_batchable(&proof_batchable_bytes).is_ok());
64+
assert!(nizk.verify_compact(&proof_compact_bytes).is_ok());
10065
}

src/tests/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@ mod composition;
22
mod relations;
33
mod spec;
44
pub mod test_utils;
5+
mod test_validation_criterias;

0 commit comments

Comments
 (0)