11#[ cfg( not( feature = "std" ) ) ]
22use ahash:: RandomState ;
3- use alloc:: boxed:: Box ;
43use alloc:: format;
54use alloc:: vec:: Vec ;
65use core:: iter;
@@ -17,6 +16,7 @@ use subtle::{Choice, ConstantTimeEq};
1716use super :: { GroupMap , GroupVar , LinearCombination , LinearRelation , ScalarTerm , ScalarVar } ;
1817use crate :: errors:: { Error , InvalidInstance } ;
1918use crate :: group:: msm:: VariableMultiScalarMul ;
19+ use crate :: serialization:: serialize_elements;
2020
2121// XXX. this definition is uncomfortably similar to LinearRelation, exception made for the weights.
2222// It'd be nice to better compress potentially duplicated code.
@@ -28,7 +28,7 @@ use crate::group::msm::VariableMultiScalarMul;
2828#[ derive( Clone , Debug , Default ) ]
2929pub struct CanonicalLinearRelation < G : PrimeGroup > {
3030 /// The image group elements (left-hand side of equations)
31- pub image : Vec < G > ,
31+ pub image : Vec < GroupVar < G > > ,
3232 /// The constraints, where each constraint is a vector of (scalar_var, group_var) pairs
3333 /// representing the right-hand side of the equation
3434 pub linear_combinations : Vec < Vec < ( ScalarVar < G > , GroupVar < G > ) > > ,
@@ -107,8 +107,12 @@ impl<G: PrimeGroup> CanonicalLinearRelation<G> {
107107 }
108108
109109 // Create new weighted group element
110+ // Use a special case for one, as this is the most common weight.
110111 let original_group_val = original_group_elements. get ( group_var) ?;
111- let weighted_group = original_group_val * weight;
112+ let weighted_group = match * weight == G :: Scalar :: ONE {
113+ true => original_group_val,
114+ false => original_group_val * weight,
115+ } ;
112116
113117 // Add to our group elements with new index (length)
114118 let new_var = self . group_elements . push ( weighted_group) ;
@@ -135,7 +139,7 @@ impl<G: PrimeGroup> CanonicalLinearRelation<G> {
135139 let group_var = weighted_term. term . elem ;
136140 let weight = & weighted_term. weight ;
137141
138- if weight. is_zero ( ) . into ( ) {
142+ if weight. is_zero_vartime ( ) {
139143 continue ; // Skip zero weights
140144 }
141145
@@ -172,7 +176,8 @@ impl<G: PrimeGroup> CanonicalLinearRelation<G> {
172176 ) ) ;
173177 }
174178
175- self . image . push ( canonical_image) ;
179+ let canonical_image_group_var = self . group_elements . push ( canonical_image) ;
180+ self . image . push ( canonical_image_group_var) ;
176181 self . linear_combinations . push ( rhs_terms) ;
177182
178183 Ok ( ( ) )
@@ -191,52 +196,18 @@ impl<G: PrimeGroup> CanonicalLinearRelation<G> {
191196 pub fn label ( & self ) -> Vec < u8 > {
192197 let mut out = Vec :: new ( ) ;
193198
194- // Create an ordered list of unique group element representations. Elements are ordered
195- // based on the order they appear in the canonical linear relation, as seen by the loop
196- // below.
197- // Order in this list is expected to be stable and lead to the same vector string.
198- // However, relations built using TryFrom<LinearRelation> are NOT guaranteed to lead
199- // to the same ordering of elements across versions of this library.
200- // Changes to LinearRelation may have unpredictable effects on how this label is built.
201- #[ cfg( feature = "std" ) ]
202- let mut group_repr_mapping: HashMap < Box < [ u8 ] > , u32 > = HashMap :: new ( ) ;
203- #[ cfg( not( feature = "std" ) ) ]
204- let mut group_repr_mapping: HashMap < Box < [ u8 ] > , u32 , RandomState > =
205- HashMap :: with_hasher ( RandomState :: new ( ) ) ;
206- let mut group_elements_ordered = Vec :: new ( ) ;
207-
208- // Helper function to get or create index for a group element representation
209- let mut repr_index = |elem_repr : G :: Repr | -> u32 {
210- if let Some ( & index) = group_repr_mapping. get ( elem_repr. as_ref ( ) ) {
211- return index;
212- }
213-
214- let new_index = group_elements_ordered. len ( ) as u32 ;
215- group_elements_ordered. push ( elem_repr) ;
216- group_repr_mapping. insert ( elem_repr. as_ref ( ) . into ( ) , new_index) ;
217- new_index
218- } ;
219-
220199 // Build constraint data in the same order as original, as a nested list of group and
221200 // scalar indices. Note that the group indices are into group_elements_ordered.
222201 let mut constraint_data = Vec :: < ( u32 , Vec < ( u32 , u32 ) > ) > :: new ( ) ;
223202
224- for ( image_elem, constraint_terms) in iter:: zip ( & self . image , & self . linear_combinations ) {
225- // First, add the left-hand side (image) element
226- let lhs_index = repr_index ( image_elem. to_bytes ( ) ) ;
227-
203+ for ( image_var, constraint_terms) in iter:: zip ( & self . image , & self . linear_combinations ) {
228204 // Build the RHS terms
229205 let mut rhs_terms = Vec :: new ( ) ;
230206 for ( scalar_var, group_var) in constraint_terms {
231- let group_elem = self
232- . group_elements
233- . get ( * group_var)
234- . expect ( "Group element not found" ) ;
235- let group_index = repr_index ( group_elem. to_bytes ( ) ) ;
236- rhs_terms. push ( ( scalar_var. 0 as u32 , group_index) ) ;
207+ rhs_terms. push ( ( scalar_var. 0 as u32 , group_var. 0 as u32 ) ) ;
237208 }
238209
239- constraint_data. push ( ( lhs_index , rhs_terms) ) ;
210+ constraint_data. push ( ( image_var . 0 as u32 , rhs_terms) ) ;
240211 }
241212
242213 // 1. Number of equations
@@ -258,10 +229,13 @@ impl<G: PrimeGroup> CanonicalLinearRelation<G> {
258229 }
259230 }
260231
261- // Dump the group elements in the order they were first encountered
262- for elem_repr in group_elements_ordered {
263- out. extend_from_slice ( elem_repr. as_ref ( ) ) ;
264- }
232+ // Dump the group elements.
233+ let group_reprs = serialize_elements (
234+ self . group_elements
235+ . iter ( )
236+ . map ( |( _, elem) | elem. expect ( "expected group variable to be assigned" ) ) ,
237+ ) ;
238+ out. extend_from_slice ( & group_reprs) ;
265239
266240 out
267241 }
@@ -410,9 +384,7 @@ impl<G: PrimeGroup> CanonicalLinearRelation<G> {
410384 // Build constraints
411385 for ( lhs_index, rhs_terms) in constraint_data {
412386 // Add image element
413- canonical
414- . image
415- . push ( group_elements_ordered[ lhs_index as usize ] ) ;
387+ canonical. image . push ( group_var_map[ lhs_index as usize ] ) ;
416388
417389 // Build linear combination
418390 let mut linear_combination = Vec :: new ( ) ;
@@ -426,6 +398,16 @@ impl<G: PrimeGroup> CanonicalLinearRelation<G> {
426398
427399 Ok ( canonical)
428400 }
401+
402+ /// Access the group elements associated with the image (i.e. left-hand side), panicking if any
403+ /// of the image variables are unassigned in the group mkap.
404+ pub ( crate ) fn image_elements ( & self ) -> impl Iterator < Item = G > + use < ' _ , G > {
405+ self . image . iter ( ) . map ( |var| {
406+ self . group_elements
407+ . get ( * var)
408+ . expect ( "expected group variable to be assigned" )
409+ } )
410+ }
429411}
430412
431413impl < G : PrimeGroup > TryFrom < LinearRelation < G > > for CanonicalLinearRelation < G > {
@@ -460,23 +442,20 @@ impl<G: PrimeGroup> TryFrom<&LinearRelation<G>> for CanonicalLinearRelation<G> {
460442 // If any group element in the image is not assigned, return `InvalidInstance`.
461443 let lhs_value = relation. linear_map . group_elements . get ( * lhs) ?;
462444
463- // If any group element in the linear constraints is not assigned, return `InvalidInstance`.
464- let rhs_elements = rhs
465- . 0
466- . iter ( )
467- . map ( |weighted| relation. linear_map . group_elements . get ( weighted. term . elem ) )
468- . collect :: < Result < Vec < G > , _ > > ( ) ?;
469-
470445 // Compute the constant terms on the right-hand side of the equation.
471- let rhs_constants = rhs
446+ // If any group element in the linear constraints is not assigned, return `InvalidInstance`.
447+ let rhs_constant_terms = rhs
472448 . 0
473449 . iter ( )
474- . map ( |element| match element. term . scalar {
475- ScalarTerm :: Unit => element. weight ,
476- _ => G :: Scalar :: ZERO ,
450+ . filter ( |term| matches ! ( term. term. scalar, ScalarTerm :: Unit ) )
451+ . map ( |term| {
452+ let elem = relation. linear_map . group_elements . get ( term. term . elem ) ?;
453+ let scalar = term. weight ;
454+ Ok ( ( elem, scalar) )
477455 } )
478- . collect :: < Vec < _ > > ( ) ;
479- let rhs_constant_term = G :: msm ( & rhs_constants, & rhs_elements) ;
456+ . collect :: < Result < ( Vec < G > , Vec < G :: Scalar > ) , _ > > ( ) ?;
457+
458+ let rhs_constant_term = G :: msm ( & rhs_constant_terms. 1 , & rhs_constant_terms. 0 ) ;
480459
481460 // We say that an equation is trivial if it contains no scalar variables.
482461 // To "contain no scalar variables" means that each term in the right-hand side is a unit or its weight is zero.
@@ -516,8 +495,7 @@ impl<G: PrimeGroup + ConstantTimeEq> CanonicalLinearRelation<G> {
516495 /// If the number of scalars is more than the number of scalar variables, the extra elements are ignored.
517496 pub fn is_witness_valid ( & self , witness : & [ G :: Scalar ] ) -> Choice {
518497 let got = self . evaluate ( witness) ;
519- self . image
520- . iter ( )
498+ self . image_elements ( )
521499 . zip ( got)
522500 . fold ( Choice :: from ( 1 ) , |acc, ( lhs, rhs) | acc & lhs. ct_eq ( & rhs) )
523501 }
0 commit comments