Skip to content

Commit fe5cbad

Browse files
committed
feat: OR/AND proofs compose LinearRelations and skip trivial branches.
1 parent 13d4cd2 commit fe5cbad

File tree

6 files changed

+185
-171
lines changed

6 files changed

+185
-171
lines changed

examples/simple_composition.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ fn create_relation(P1: G, P2: G, Q: G, H: G) -> ComposedRelation<G> {
4040
rel2.set_element(Q_var, Q);
4141

4242
// Compose into OR protocol
43-
ComposedRelation::or([rel1.canonical().unwrap(), rel2.canonical().unwrap()])
43+
ComposedRelation::or([rel1.canonical().unwrap(), rel2.canonical().unwrap()]).unwrap()
4444
}
4545

4646
/// Prove knowledge of one of the witnesses (we know x2 for the DLEQ)

src/composition.rs

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,13 +48,34 @@ pub enum ComposedRelation<G: PrimeGroup> {
4848

4949
impl<G: PrimeGroup + ConstantTimeEq> ComposedRelation<G> {
5050
/// Create a [ComposedRelation] for an AND relation from the given list of relations.
51-
pub fn and<T: Into<ComposedRelation<G>>>(witness: impl IntoIterator<Item = T>) -> Self {
52-
Self::And(witness.into_iter().map(|x| x.into()).collect())
51+
pub fn and<T>(instances: impl IntoIterator<Item = T>) -> Result<Self, InvalidInstance>
52+
where
53+
T: TryInto<ComposedRelation<G>>,
54+
T::Error: Into<InvalidInstance>,
55+
{
56+
instances
57+
.into_iter()
58+
.map(|x| x.try_into().map_err(|e| e.into()))
59+
.collect::<Result<Vec<ComposedRelation<G>>, _>>()
60+
.map(Self::And)
5361
}
5462

5563
/// Create a [ComposedRelation] for an OR relation from the given list of relations.
56-
pub fn or<T: Into<ComposedRelation<G>>>(witness: impl IntoIterator<Item = T>) -> Self {
57-
Self::Or(witness.into_iter().map(|x| x.into()).collect())
64+
pub fn or<T>(instances: impl IntoIterator<Item = T>) -> Result<Self, InvalidInstance>
65+
where
66+
T: TryInto<ComposedRelation<G>>,
67+
T::Error: Into<InvalidInstance>,
68+
{
69+
let composed_instances = instances
70+
.into_iter()
71+
.filter_map(|x| x.try_into().ok())
72+
.collect::<Vec<_>>();
73+
74+
if composed_instances.is_empty() {
75+
Err(InvalidInstance::new("All Instances are trivially false"))
76+
} else {
77+
Ok(Self::Or(composed_instances))
78+
}
5879
}
5980
}
6081

@@ -72,6 +93,14 @@ impl<G: PrimeGroup> TryFrom<LinearRelation<G>> for ComposedRelation<G> {
7293
}
7394
}
7495

96+
impl<G: PrimeGroup> TryFrom<Result<ComposedRelation<G>, InvalidInstance>> for ComposedRelation<G> {
97+
type Error = InvalidInstance;
98+
99+
fn try_from(value: Result<ComposedRelation<G>, InvalidInstance>) -> Result<Self, Self::Error> {
100+
value
101+
}
102+
}
103+
75104
// Structure representing the Commitment type of Protocol as SigmaProtocol
76105
#[derive(Clone)]
77106
pub enum ComposedCommitment<G: PrimeGroup> {

src/errors.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
//! - Mismatched parameter lengths (e.g., during batch verification),
99
//! - Access to unassigned group variables in constraint systems.
1010
11+
use core::convert::Infallible;
12+
1113
/// Represents an invalid instance error.
1214
#[derive(Debug, thiserror::Error)]
1315
#[error("Invalid instance: {message}")]
@@ -31,6 +33,13 @@ impl From<InvalidInstance> for Error {
3133
}
3234
}
3335

36+
impl From<Infallible> for InvalidInstance {
37+
fn from(_: Infallible) -> Self {
38+
// This conversion is never actually called since Infallible can never be instantiated
39+
unreachable!("Infallible can never be instantiated")
40+
}
41+
}
42+
3443
/// Represents an error encountered during the execution of a Sigma protocol.
3544
///
3645
/// This may occur during proof generation, response computation, or verification.

src/tests/test_composition.rs

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -12,33 +12,34 @@ fn test_composition_example() {
1212
// Composition and verification of proof for the following protocol :
1313
//
1414
// And(
15-
// Or( dleq, pedersen_commitment ),
16-
// Simple( discrete_logarithm ),
17-
// And( pedersen_commitment_dleq, bbs_blind_commitment_computation )
15+
// Or(dleq, pedersen_commitment),
16+
// Simple(discrete_logarithm),
17+
// And(pedersen_commitment_dleq, bbs_blind_commitment_computation)
1818
// )
1919
let domain_sep = b"hello world";
2020

2121
// definitions of the underlying protocols
2222
let mut rng = rand::thread_rng();
23-
let (relation1, witness1) = dleq(&mut rng);
24-
let (relation2, witness2) = pedersen_commitment(&mut rng);
25-
let (relation3, witness3) = discrete_logarithm(&mut rng);
26-
let (relation4, witness4) = pedersen_commitment_dleq(&mut rng);
27-
let (relation5, witness5) = bbs_blind_commitment(&mut rng);
23+
let (instance1, witness1) = dleq::<G, _>(&mut rng);
24+
let (instance2, witness2) = pedersen_commitment::<G, _>(&mut rng);
25+
let (instance3, witness3) = discrete_logarithm::<G, _>(&mut rng);
26+
let (instance4, witness4) = pedersen_commitment_dleq::<G, _>(&mut rng);
27+
let (instance5, witness5) = bbs_blind_commitment::<G, _>(&mut rng);
2828

2929
let wrong_witness2 = (0..witness2.len())
3030
.map(|_| <G as Group>::Scalar::random(&mut rng))
3131
.collect::<Vec<_>>();
3232
// second layer protocol definitions
33-
let or_protocol1 = ComposedRelation::<G>::or([relation1, relation2]);
33+
let or_protocol1 = ComposedRelation::or([instance1, instance2]);
3434
let or_witness1 = ComposedWitness::or([witness1, wrong_witness2]);
3535

36-
let and_protocol1 = ComposedRelation::and([relation4, relation5]);
36+
let and_protocol1 = ComposedRelation::and([instance4, instance5]);
3737
let and_witness1 = ComposedWitness::and([witness4, witness5]);
3838

3939
// definition of the final protocol
40-
let instance = ComposedRelation::and([or_protocol1, relation3.into(), and_protocol1]);
41-
let witness = ComposedWitness::and([or_witness1, witness3.into(), and_witness1]);
40+
let instance =
41+
ComposedRelation::and([or_protocol1, instance3.try_into(), and_protocol1]).unwrap();
42+
let witness = ComposedWitness::and([or_witness1, witness3.try_into().unwrap(), and_witness1]);
4243

4344
let nizk = instance.into_nizk(domain_sep);
4445

@@ -67,13 +68,13 @@ fn test_or_one_true() {
6768
.map(|_| <G as Group>::Scalar::random(&mut rng))
6869
.collect::<Vec<_>>();
6970

70-
let or_protocol = ComposedRelation::or([relation1, relation2]);
71+
let or_instance = ComposedRelation::or([relation1, relation2]).unwrap();
7172

7273
// Construct two witnesses to the protocol, the first and then the second as the true branch.
7374
let witness_or_1 = ComposedWitness::or([witness1, wrong_witness2]);
7475
let witness_or_2 = ComposedWitness::or([wrong_witness1, witness2]);
7576

76-
let nizk = or_protocol.into_nizk(b"test_or_one_true");
77+
let nizk = or_instance.into_nizk(b"test_or_one_true");
7778

7879
for witness in [witness_or_1, witness_or_2] {
7980
// Batchable and compact proofs
@@ -95,7 +96,7 @@ fn test_or_both_true() {
9596
let (relation1, witness1) = dleq::<G, _>(&mut rng);
9697
let (relation2, witness2) = dleq::<G, _>(&mut rng);
9798

98-
let or_protocol = ComposedRelation::or([relation1, relation2]);
99+
let or_protocol = ComposedRelation::or([relation1, relation2]).unwrap();
99100

100101
let witness = ComposedWitness::or([witness1, witness2]);
101102
let nizk = or_protocol.into_nizk(b"test_or_both_true");

0 commit comments

Comments
 (0)