Skip to content

Commit fbfb29a

Browse files
committed
Rework validation criterias for accepting relations.
1 parent 995d2f9 commit fbfb29a

File tree

6 files changed

+121
-75
lines changed

6 files changed

+121
-75
lines changed

src/composition.rs

Lines changed: 29 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -246,14 +246,23 @@ impl<G: PrimeGroup + ConstantTimeEq> ComposedRelation<G> {
246246
let valid_witness = instances[i].is_witness_valid(w);
247247
let select_witness = valid_witness & !valid_witness_found;
248248

249-
let simulated_commitment_ptr = &simulated_commitment as *const ComposedCommitment<G> as u64;
250-
let commitment_ptr = &commitment as *const ComposedCommitment<G> as u64;
249+
let simulated_commitment_ptr =
250+
&simulated_commitment as *const ComposedCommitment<G> as u64;
251+
let commitment_ptr = &commitment as *const ComposedCommitment<G> as u64;
251252

252-
let selected_commitment_ptr = ConditionallySelectable::conditional_select(&simulated_commitment_ptr, &commitment_ptr, select_witness);
253-
let discarded_commitment_ptr = ConditionallySelectable::conditional_select(&simulated_commitment_ptr, &commitment_ptr, !select_witness);
253+
let selected_commitment_ptr = ConditionallySelectable::conditional_select(
254+
&simulated_commitment_ptr,
255+
&commitment_ptr,
256+
select_witness,
257+
);
258+
let discarded_commitment_ptr = ConditionallySelectable::conditional_select(
259+
&simulated_commitment_ptr,
260+
&commitment_ptr,
261+
!select_witness,
262+
);
254263
let commitment = unsafe { &*(selected_commitment_ptr as *const ComposedCommitment<G>) };
255-
let _discarded = unsafe { &*(discarded_commitment_ptr as *const ComposedCommitment<G>) };
256-
264+
let _discarded =
265+
unsafe { &*(discarded_commitment_ptr as *const ComposedCommitment<G>) };
257266

258267
commitments.push(commitment.clone());
259268
prover_states.push(ComposedOrProverStateEntry(
@@ -318,11 +327,19 @@ impl<G: PrimeGroup + ConstantTimeEq> ComposedRelation<G> {
318327
let response = instance.prover_response(prover_state, &challenge_i)?;
319328
let response_ptr = &response as *const ComposedResponse<G> as u64;
320329
let simulated_response_ptr = &simulated_response as *const ComposedResponse<G> as u64;
321-
let selected_response_ptr = ConditionallySelectable::conditional_select(&simulated_response_ptr, &response_ptr, valid_witness);
322-
let _discarded_response_ptr = ConditionallySelectable::conditional_select(&simulated_response_ptr, &response_ptr, !valid_witness);
330+
let selected_response_ptr = ConditionallySelectable::conditional_select(
331+
&simulated_response_ptr,
332+
&response_ptr,
333+
valid_witness,
334+
);
335+
let _discarded_response_ptr = ConditionallySelectable::conditional_select(
336+
&simulated_response_ptr,
337+
&response_ptr,
338+
!valid_witness,
339+
);
323340
let response = unsafe { &*(selected_response_ptr as *const ComposedResponse<G>) };
324-
let _discarded_response = unsafe { &*(_discarded_response_ptr as *const ComposedResponse<G>) };
325-
341+
let _discarded_response =
342+
unsafe { &*(_discarded_response_ptr as *const ComposedResponse<G>) };
326343

327344
result_challenges.push(challenge_i);
328345
result_responses.push(response.clone());
@@ -333,9 +350,7 @@ impl<G: PrimeGroup + ConstantTimeEq> ComposedRelation<G> {
333350
}
334351
}
335352

336-
impl<G: PrimeGroup + ConstantTimeEq> SigmaProtocol
337-
for ComposedRelation<G>
338-
{
353+
impl<G: PrimeGroup + ConstantTimeEq> SigmaProtocol for ComposedRelation<G> {
339354
type Commitment = ComposedCommitment<G>;
340355
type ProverState = ComposedProverState<G>;
341356
type Response = ComposedResponse<G>;
@@ -587,9 +602,7 @@ impl<G: PrimeGroup + ConstantTimeEq> SigmaProtocol
587602
}
588603
}
589604

590-
impl<G: PrimeGroup + ConstantTimeEq> SigmaProtocolSimulator
591-
for ComposedRelation<G>
592-
{
605+
impl<G: PrimeGroup + ConstantTimeEq> SigmaProtocolSimulator for ComposedRelation<G> {
593606
fn simulate_commitment(
594607
&self,
595608
challenge: &Self::Challenge,

src/lib.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,6 @@ pub mod errors;
5656
pub mod linear_relation;
5757
pub mod traits;
5858

59-
6059
pub(crate) mod duplex_sponge;
6160
pub(crate) mod fiat_shamir;
6261
pub(crate) mod group;
@@ -69,4 +68,4 @@ pub use fiat_shamir::Nizk;
6968
pub use linear_relation::LinearRelation;
7069

7170
#[deprecated = "Use sigma_rs::group::serialization instead"]
72-
pub use group::serialization;
71+
pub use group::serialization;

src/linear_relation/canonical.rs

Lines changed: 34 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -429,36 +429,40 @@ impl<G: PrimeGroup> TryFrom<&LinearRelation<G>> for CanonicalLinearRelation<G> {
429429

430430
// Process each constraint using the modular helper method
431431
for (lhs, rhs) in iter::zip(&relation.image, &relation.linear_map.linear_combinations) {
432-
let lhs_value = relation
433-
.linear_map
434-
.group_elements
435-
.get(*lhs)
436-
.map_err(|_| InvalidInstance::new("Unassigned group variable in image"))?;
437-
438-
// If the linear combination is trivial, check it directly and skip processing.
439-
if rhs.0.iter().all(|weighted| {
440-
matches!(weighted.term.scalar, ScalarTerm::Unit)
441-
|| weighted.weight.is_zero_vartime()
442-
}) {
443-
let rhs_value = rhs.0.iter().fold(G::identity(), |acc, weighted| {
444-
acc + relation
445-
.linear_map
446-
.group_elements
447-
.get(weighted.term.elem)
448-
.unwrap_or_else(|_| {
449-
panic!("Unassigned group variable in linear combination")
450-
})
451-
* weighted.weight
452-
});
453-
if lhs_value == rhs_value {
454-
continue; // Skip processing trivially true constraints
455-
}
456-
// We know that there is no valid witness for the relation here.
457-
// return Err(InvalidInstance::new(
458-
// "Trivial constraint does not hold (LHS != RHS)",
459-
// ));
460-
} else if lhs_value == G::identity() {
461-
return Err(InvalidInstance::new("Image contains identity element"));
432+
// If any group element in the image is not assigned, return `InvalidInstance`.
433+
let lhs_value = relation.linear_map.group_elements.get(*lhs)?;
434+
435+
// If any group element in the linear constraints is not assigned, return `InvalidInstance`.
436+
let rhs_elements = rhs
437+
.0
438+
.iter()
439+
.map(|weighted| relation.linear_map.group_elements.get(weighted.term.elem))
440+
.collect::<Result<Vec<G>, _>>()?;
441+
442+
// Compute the constant terms on the right-hand side of the equation.
443+
let rhs_constants = rhs
444+
.0
445+
.iter()
446+
.map(|element| match element.term.scalar {
447+
ScalarTerm::Unit => element.weight,
448+
_ => G::Scalar::ZERO,
449+
})
450+
.collect::<Vec<_>>();
451+
let rhs_constant_term = G::msm(&rhs_constants, &rhs_elements);
452+
453+
// The right-hand side is trivial if it contains no scalar variables, or the weight is zero.
454+
let is_trivial = rhs.0.iter().all(|term| {
455+
matches!(term.term.scalar, ScalarTerm::Unit) || term.weight.is_zero_vartime()
456+
});
457+
458+
// Skip processing trivially true constraints. There's nothing to prove here.
459+
if is_trivial && rhs_constant_term == lhs_value {
460+
continue;
461+
}
462+
463+
// Disallow non-trivial equations with trivial solutions.
464+
if !is_trivial && rhs_constant_term == lhs_value {
465+
return Err(InvalidInstance::new("Trivial kernel in this relation"));
462466
}
463467

464468
canonical.process_constraint(lhs, rhs, relation, &mut weighted_group_cache)?;

src/linear_relation/mod.rs

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -196,8 +196,10 @@ impl<G: PrimeGroup> GroupMap<G> {
196196
pub fn get(&self, var: GroupVar<G>) -> Result<G, InvalidInstance> {
197197
match self.0.get(var.0) {
198198
Some(Some(elem)) => Ok(*elem),
199-
Some(None) => Err(InvalidInstance::new("unassigned group variable")),
200-
None => Err(InvalidInstance::new("unassigned group variable")),
199+
Some(None) | None => Err(InvalidInstance::new(format!(
200+
"unassigned group variable {}",
201+
var.0
202+
))),
201203
}
202204
}
203205

@@ -409,6 +411,13 @@ impl<G: PrimeGroup> LinearRelation<G> {
409411
GroupVar(self.linear_map.num_elements - 1, PhantomData)
410412
}
411413

414+
/// Allocates a point variable (group element) and sets it immediately to the given value
415+
pub fn allocate_element_with(&mut self, element: G) -> GroupVar<G> {
416+
let var = self.allocate_element();
417+
self.set_element(var, element);
418+
var
419+
}
420+
412421
/// Allocates `N` point variables (group elements) for use in the linear map.
413422
///
414423
/// # Returns
@@ -431,6 +440,14 @@ impl<G: PrimeGroup> LinearRelation<G> {
431440
vars
432441
}
433442

443+
/// Allocates a point variable (group element) and sets it immediately to the given value.
444+
pub fn allocate_elements_with(&mut self, elements: &[G]) -> Vec<GroupVar<G>> {
445+
elements
446+
.iter()
447+
.map(|element| self.allocate_element_with(*element))
448+
.collect()
449+
}
450+
434451
/// Assign a group element value to a point variable.
435452
///
436453
/// # Parameters

src/linear_relation/ops.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -571,9 +571,9 @@ mod sub {
571571
#[cfg(test)]
572572
mod tests {
573573
use crate::linear_relation::{GroupVar, ScalarTerm, ScalarVar, Term};
574+
use core::marker::PhantomData;
574575
use curve25519_dalek::RistrettoPoint as G;
575576
use curve25519_dalek::Scalar;
576-
use core::marker::PhantomData;
577577

578578
fn scalar_var(i: usize) -> ScalarVar<G> {
579579
ScalarVar(i, PhantomData)

src/tests/test_validation_criteria.rs

Lines changed: 37 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
mod instance_validation {
88
use crate::linear_relation::{CanonicalLinearRelation, LinearRelation};
99
use bls12_381::{G1Projective as G, Scalar};
10+
use ff::Field;
11+
use group::Group;
1012

1113
#[test]
1214
fn test_unassigned_group_vars() {
@@ -67,8 +69,6 @@ mod instance_validation {
6769
#[test]
6870
#[allow(non_snake_case)]
6971
pub fn test_degenerate_equation() {
70-
use ff::Field;
71-
7272
// This relation should fail for two reasons:
7373
// 1. because var_B is not assigned
7474
let mut relation = LinearRelation::<G>::new();
@@ -137,57 +137,70 @@ mod instance_validation {
137137
let mut linear_relation = LinearRelation::<G>::new();
138138
let B_var = linear_relation.allocate_element();
139139
let C_var = linear_relation.allocate_eq(B_var);
140-
linear_relation.set_element(B_var, B);
141-
linear_relation.set_element(C_var, C);
140+
linear_relation.set_elements([(B_var, B), (C_var, C)]);
142141
assert!(linear_relation.canonical().is_ok());
143142

144143
// Also in this case, we know that no witness will ever satisfy the relation.
145144
// Also here, the relation is built even though the prover will never be able to give a valid proof for it.
146145
// X != B * pub_scalar + A * 3
147146
let mut linear_relation = LinearRelation::<G>::new();
148-
let B_var = linear_relation.allocate_element();
149-
let A_var = linear_relation.allocate_element();
147+
let [B_var, A_var] = linear_relation.allocate_elements();
150148
let X_var = linear_relation.allocate_eq(B_var * pub_scalar + A_var * Scalar::from(3));
151-
152-
linear_relation.set_element(B_var, B);
153-
linear_relation.set_element(A_var, A);
154-
linear_relation.set_element(X_var, X);
149+
linear_relation.set_elements([(B_var, B), (A_var, A), (X_var, X)]);
155150
assert!(linear_relation.canonical().is_ok());
156151

157152
// The following relation is valid and should pass.
158153
let mut linear_relation = LinearRelation::<G>::new();
159154
let B_var = linear_relation.allocate_element();
160155
let C_var = linear_relation.allocate_eq(B_var);
161-
linear_relation.set_element(B_var, B);
162-
linear_relation.set_element(C_var, B);
156+
linear_relation.set_elements([(B_var, B), (C_var, B)]);
163157
assert!(linear_relation.canonical().is_ok());
164158

165159
// The following relation is valid and should pass.
166160
// C = B * pub_scalar + A * 3
167161
let mut linear_relation = LinearRelation::<G>::new();
168-
let B_var = linear_relation.allocate_element();
169-
let A_var = linear_relation.allocate_element();
162+
let [B_var, A_var] = linear_relation.allocate_elements();
170163
let C_var = linear_relation.allocate_eq(B_var * pub_scalar + A_var * Scalar::from(3));
171-
172-
linear_relation.set_element(B_var, B);
173-
linear_relation.set_element(A_var, A);
174-
linear_relation.set_element(C_var, C);
164+
linear_relation.set_elements([(B_var, B), (A_var, A), (C_var, C)]);
175165
assert!(linear_relation.canonical().is_ok());
176166

177167
// The following relation is for
178168
// X = B * x + B * pub_scalar + A * 3
179169
// and should be considered a valid instance.
180170
let mut linear_relation = LinearRelation::<G>::new();
181-
182171
let x_var = linear_relation.allocate_scalar();
183-
let B_var = linear_relation.allocate_element();
184-
let A_var = linear_relation.allocate_element();
172+
let [B_var, A_var] = linear_relation.allocate_elements();
185173
let X_var = linear_relation
186174
.allocate_eq(B_var * x_var + B_var * pub_scalar + A_var * Scalar::from(3));
175+
linear_relation.set_elements([(B_var, B), (A_var, A), (X_var, X)]);
176+
assert!(linear_relation.canonical().is_ok());
177+
}
178+
179+
// Create a relation with a zero image, such as
180+
// 0 = x*A + y*B + C
181+
// It should be accepted.
182+
#[test]
183+
fn test_statement_with_trivial_image() {
184+
let mut rng = rand::thread_rng();
185+
let mut linear_relation = LinearRelation::new();
186+
let [x_var, y_var] = linear_relation.allocate_scalars();
187+
let [Z_var, A_var, B_var, C_var] = linear_relation.allocate_elements();
188+
linear_relation.append_equation(Z_var, x_var * A_var + y_var * B_var + C_var);
189+
let [x, y] = [Scalar::random(&mut rng), Scalar::random(&mut rng)];
190+
let Z = G::identity();
191+
let A = G::random(&mut rng);
192+
let B = G::generator();
193+
let C = -x * A - y * B;
194+
195+
linear_relation.set_elements([(Z_var, Z), (A_var, A), (B_var, B), (C_var, C)]);
196+
assert!(linear_relation.canonical().is_ok());
187197

188-
linear_relation.set_element(B_var, B);
189-
linear_relation.set_element(A_var, A);
190-
linear_relation.set_element(X_var, X);
198+
let F_var = linear_relation.allocate_element();
199+
let f_var = linear_relation.allocate_scalar();
200+
linear_relation.append_equation(F_var, f_var * A_var);
201+
let f = Scalar::random(&mut rng);
202+
let F = A * f;
203+
linear_relation.set_elements([(F_var, F), (A_var, A)]);
191204
assert!(linear_relation.canonical().is_ok());
192205
}
193206
}

0 commit comments

Comments
 (0)