@@ -25,6 +25,7 @@ type Field = <console::TestnetV0 as console::Environment>::Field;
2525thread_local ! {
2626 static VARIABLE_LIMIT : Cell <Option <u64 >> = const { Cell :: new( None ) } ;
2727 static CONSTRAINT_LIMIT : Cell <Option <u64 >> = const { Cell :: new( None ) } ;
28+ static NON_ZERO_LIMIT : Cell <Option <( u64 , u64 , u64 ) >> = const { Cell :: new( None ) } ;
2829 pub ( super ) static TESTNET_CIRCUIT : RefCell <R1CS <Field >> = RefCell :: new( R1CS :: new( ) ) ;
2930 static IN_WITNESS : Cell <bool > = const { Cell :: new( false ) } ;
3031 static ZERO : LinearCombination <Field > = LinearCombination :: zero( ) ;
@@ -58,6 +59,8 @@ impl Environment for TestnetCircuit {
5859 // Ensure that we do not surpass the variable limit for the circuit.
5960 VARIABLE_LIMIT . with ( |variable_limit| {
6061 if let Some ( limit) = variable_limit. get ( ) {
62+ // Note: because we check > here, one could theoretically construct a
63+ // circuit with variable_limit + 1 variables.
6164 if Self :: num_variables ( ) > limit {
6265 Self :: halt ( format ! ( "Surpassed the variable limit ({limit})" ) )
6366 }
@@ -136,6 +139,8 @@ impl Environment for TestnetCircuit {
136139 // Ensure that we do not surpass the constraint limit for the circuit.
137140 CONSTRAINT_LIMIT . with ( |constraint_limit| {
138141 if let Some ( limit) = constraint_limit. get ( ) {
142+ // Note: because we check > here, one could theoretically construct a
143+ // circuit with constraint_limit + 1 constraints.
139144 if circuit. borrow ( ) . num_constraints ( ) > limit {
140145 Self :: halt ( format ! ( "Surpassed the constraint limit ({limit})" ) )
141146 }
@@ -160,6 +165,23 @@ impl Environment for TestnetCircuit {
160165 false => {
161166 // Construct the constraint object.
162167 let constraint = Constraint ( circuit. borrow ( ) . scope ( ) , a, b, c) ;
168+
169+ // Ensure that we do not surpass the density limit for the circuit.
170+ NON_ZERO_LIMIT . with ( |non_zero_limit| {
171+ if let Some ( ( limit_a, limit_b, limit_c) ) = non_zero_limit. get ( ) {
172+ let ( curr_d_a, curr_d_b, curr_d_c) = circuit. borrow ( ) . num_nonzeros ( ) ;
173+ let ( d_a, d_b, d_c) = constraint. num_nonzeros ( ) ;
174+ if curr_d_a. saturating_add ( d_a) > limit_a
175+ || curr_d_b. saturating_add ( d_b) > limit_b
176+ || curr_d_c. saturating_add ( d_c) > limit_c
177+ {
178+ Self :: halt ( format ! (
179+ "Surpassed the circuit density limit (A: {limit_a}, B: {limit_b}, C: {limit_c}). Was ({curr_d_a}, {curr_d_b}, {curr_d_c}) before, tried to add ({d_a}, {d_b}, {d_c})"
180+ ) )
181+ }
182+ }
183+ } ) ;
184+
163185 // Append the constraint.
164186 circuit. borrow_mut ( ) . enforce ( constraint)
165187 }
@@ -257,6 +279,16 @@ impl Environment for TestnetCircuit {
257279 CONSTRAINT_LIMIT . with ( |current_limit| current_limit. replace ( limit) ) ;
258280 }
259281
282+ /// Returns the density limit for the circuit, if one exists.
283+ fn get_non_zero_limit ( ) -> Option < ( u64 , u64 , u64 ) > {
284+ NON_ZERO_LIMIT . with ( |current_limit| current_limit. get ( ) )
285+ }
286+
287+ /// Sets the density limit for the circuit.
288+ fn set_non_zero_limit ( limit : Option < ( u64 , u64 , u64 ) > ) {
289+ NON_ZERO_LIMIT . with ( |current_limit| current_limit. replace ( limit) ) ;
290+ }
291+
260292 /// Halts the program from further synthesis, evaluation, and execution in the current environment.
261293 fn halt < S : Into < String > , T > ( message : S ) -> T {
262294 let error = message. into ( ) ;
@@ -293,6 +325,8 @@ impl Environment for TestnetCircuit {
293325 Self :: set_variable_limit ( None ) ;
294326 // Reset the constraint limit.
295327 Self :: set_constraint_limit ( None ) ;
328+ // Reset the density limit.
329+ Self :: set_non_zero_limit ( None ) ;
296330 // Eject the R1CS instance.
297331 let r1cs = circuit. replace ( R1CS :: < <Self as Environment >:: BaseField > :: new ( ) ) ;
298332 // Ensure the circuit is now empty.
@@ -315,6 +349,8 @@ impl Environment for TestnetCircuit {
315349 Self :: set_variable_limit ( None ) ;
316350 // Reset the constraint limit.
317351 Self :: set_constraint_limit ( None ) ;
352+ // Reset the density limit.
353+ Self :: set_non_zero_limit ( None ) ;
318354 // Eject the R1CS instance.
319355 let r1cs = circuit. replace ( R1CS :: < <Self as Environment >:: BaseField > :: new ( ) ) ;
320356 assert_eq ! ( 0 , circuit. borrow( ) . num_constants( ) ) ;
@@ -336,6 +372,8 @@ impl Environment for TestnetCircuit {
336372 Self :: set_variable_limit ( None ) ;
337373 // Reset the constraint limit.
338374 Self :: set_constraint_limit ( None ) ;
375+ // Reset the density limit.
376+ Self :: set_non_zero_limit ( None ) ;
339377 // Reset the circuit.
340378 * circuit. borrow_mut ( ) = R1CS :: < <Self as Environment >:: BaseField > :: new ( ) ;
341379 assert_eq ! ( 0 , circuit. borrow( ) . num_constants( ) ) ;
0 commit comments