snarkvm_circuit_environment/
circuit.rs1use crate::{Mode, helpers::Constraint, *};
17
18use core::{
19 cell::{Cell, RefCell},
20 fmt,
21};
22
23type Field = <console::MainnetV0 as console::Environment>::Field;
24
25thread_local! {
26 static VARIABLE_LIMIT: Cell<Option<u64>> = const { Cell::new(None) };
27 static CONSTRAINT_LIMIT: Cell<Option<u64>> = const { Cell::new(None) };
28 pub(super) static CIRCUIT: RefCell<R1CS<Field>> = RefCell::new(R1CS::new());
29 static IN_WITNESS: Cell<bool> = const { Cell::new(false) };
30 static ZERO: LinearCombination<Field> = LinearCombination::zero();
31 static ONE: LinearCombination<Field> = LinearCombination::one();
32}
33
34#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
35pub struct Circuit;
36
37impl Environment for Circuit {
38 type Affine = <console::MainnetV0 as console::Environment>::Affine;
39 type BaseField = Field;
40 type Network = console::MainnetV0;
41 type ScalarField = <console::MainnetV0 as console::Environment>::Scalar;
42
43 fn zero() -> LinearCombination<Self::BaseField> {
45 ZERO.with(|zero| zero.clone())
46 }
47
48 fn one() -> LinearCombination<Self::BaseField> {
50 ONE.with(|one| one.clone())
51 }
52
53 fn new_variable(mode: Mode, value: Self::BaseField) -> Variable<Self::BaseField> {
55 IN_WITNESS.with(|in_witness| {
56 if !in_witness.get() {
58 VARIABLE_LIMIT.with(|variable_limit| {
60 if let Some(limit) = variable_limit.get() {
61 if Self::num_variables() > limit {
62 Self::halt(format!("Surpassed the variable limit ({limit})"))
63 }
64 }
65 });
66 CIRCUIT.with(|circuit| match mode {
67 Mode::Constant => circuit.borrow_mut().new_constant(value),
68 Mode::Public => circuit.borrow_mut().new_public(value),
69 Mode::Private => circuit.borrow_mut().new_private(value),
70 })
71 } else {
72 Self::halt("Tried to initialize a new variable in witness mode")
73 }
74 })
75 }
76
77 fn new_witness<Fn: FnOnce() -> Output::Primitive, Output: Inject>(mode: Mode, logic: Fn) -> Output {
79 IN_WITNESS.with(|in_witness| {
80 in_witness.replace(true);
82
83 let output = logic();
85
86 in_witness.replace(false);
88
89 Inject::new(mode, output)
90 })
91 }
92
93 fn scope<S: Into<String>, Fn, Output>(name: S, logic: Fn) -> Output
119 where
120 Fn: FnOnce() -> Output,
121 {
122 IN_WITNESS.with(|in_witness| {
123 if !in_witness.get() {
125 CIRCUIT.with(|circuit| {
126 let name = name.into();
128 if let Err(error) = circuit.borrow_mut().push_scope(&name) {
129 Self::halt(error)
130 }
131
132 let output = logic();
134
135 if let Err(error) = circuit.borrow_mut().pop_scope(name) {
137 Self::halt(error)
138 }
139
140 output
141 })
142 } else {
143 Self::halt("Tried to initialize a new scope in witness mode")
144 }
145 })
146 }
147
148 fn enforce<Fn, A, B, C>(constraint: Fn)
150 where
151 Fn: FnOnce() -> (A, B, C),
152 A: Into<LinearCombination<Self::BaseField>>,
153 B: Into<LinearCombination<Self::BaseField>>,
154 C: Into<LinearCombination<Self::BaseField>>,
155 {
156 IN_WITNESS.with(|in_witness| {
157 if !in_witness.get() {
159 CIRCUIT.with(|circuit| {
160 CONSTRAINT_LIMIT.with(|constraint_limit| {
162 if let Some(limit) = constraint_limit.get() {
163 if circuit.borrow().num_constraints() > limit {
164 Self::halt(format!("Surpassed the constraint limit ({limit})"))
165 }
166 }
167 });
168
169 let (a, b, c) = constraint();
170 let (a, b, c) = (a.into(), b.into(), c.into());
171
172 match a.is_constant() && b.is_constant() && c.is_constant() {
174 true => {
175 assert_eq!(
177 a.value() * b.value(),
178 c.value(),
179 "Constant constraint failed: ({a} * {b}) =?= {c}"
180 );
181
182 }
190 false => {
191 let constraint = Constraint(circuit.borrow().scope(), a, b, c);
193 circuit.borrow_mut().enforce(constraint)
195 }
196 }
197 });
198 } else {
199 Self::halt("Tried to add a new constraint in witness mode")
200 }
201 })
202 }
203
204 fn is_satisfied() -> bool {
206 CIRCUIT.with(|circuit| circuit.borrow().is_satisfied())
207 }
208
209 fn is_satisfied_in_scope() -> bool {
211 CIRCUIT.with(|circuit| circuit.borrow().is_satisfied_in_scope())
212 }
213
214 fn num_constants() -> u64 {
216 CIRCUIT.with(|circuit| circuit.borrow().num_constants())
217 }
218
219 fn num_public() -> u64 {
221 CIRCUIT.with(|circuit| circuit.borrow().num_public())
222 }
223
224 fn num_private() -> u64 {
226 CIRCUIT.with(|circuit| circuit.borrow().num_private())
227 }
228
229 fn num_variables() -> u64 {
231 CIRCUIT.with(|circuit| circuit.borrow().num_variables())
232 }
233
234 fn num_constraints() -> u64 {
236 CIRCUIT.with(|circuit| circuit.borrow().num_constraints())
237 }
238
239 fn num_nonzeros() -> (u64, u64, u64) {
241 CIRCUIT.with(|circuit| circuit.borrow().num_nonzeros())
242 }
243
244 fn num_constants_in_scope() -> u64 {
246 CIRCUIT.with(|circuit| circuit.borrow().num_constants_in_scope())
247 }
248
249 fn num_public_in_scope() -> u64 {
251 CIRCUIT.with(|circuit| circuit.borrow().num_public_in_scope())
252 }
253
254 fn num_private_in_scope() -> u64 {
256 CIRCUIT.with(|circuit| circuit.borrow().num_private_in_scope())
257 }
258
259 fn num_constraints_in_scope() -> u64 {
261 CIRCUIT.with(|circuit| circuit.borrow().num_constraints_in_scope())
262 }
263
264 fn num_nonzeros_in_scope() -> (u64, u64, u64) {
266 CIRCUIT.with(|circuit| circuit.borrow().num_nonzeros_in_scope())
267 }
268
269 fn get_variable_limit() -> Option<u64> {
271 VARIABLE_LIMIT.with(|current_limit| current_limit.get())
272 }
273
274 fn set_variable_limit(limit: Option<u64>) {
276 VARIABLE_LIMIT.with(|current_limit| current_limit.replace(limit));
277 }
278
279 fn get_constraint_limit() -> Option<u64> {
281 CONSTRAINT_LIMIT.with(|current_limit| current_limit.get())
282 }
283
284 fn set_constraint_limit(limit: Option<u64>) {
286 CONSTRAINT_LIMIT.with(|current_limit| current_limit.replace(limit));
287 }
288
289 fn halt<S: Into<String>, T>(message: S) -> T {
291 let error = message.into();
292 panic!("{}", &error)
294 }
295
296 fn inject_r1cs(r1cs: R1CS<Self::BaseField>) {
298 CIRCUIT.with(|circuit| {
299 assert_eq!(0, circuit.borrow().num_constants());
301 assert_eq!(1, circuit.borrow().num_public());
302 assert_eq!(0, circuit.borrow().num_private());
303 assert_eq!(1, circuit.borrow().num_variables());
304 assert_eq!(0, circuit.borrow().num_constraints());
305 let r1cs = circuit.replace(r1cs);
307 assert_eq!(0, r1cs.num_constants());
309 assert_eq!(1, r1cs.num_public());
310 assert_eq!(0, r1cs.num_private());
311 assert_eq!(1, r1cs.num_variables());
312 assert_eq!(0, r1cs.num_constraints());
313 })
314 }
315
316 fn eject_r1cs_and_reset() -> R1CS<Self::BaseField> {
318 CIRCUIT.with(|circuit| {
319 IN_WITNESS.with(|in_witness| in_witness.replace(false));
321 Self::set_variable_limit(None);
323 Self::set_constraint_limit(None);
325 let r1cs = circuit.replace(R1CS::<<Self as Environment>::BaseField>::new());
327 assert_eq!(0, circuit.borrow().num_constants());
329 assert_eq!(1, circuit.borrow().num_public());
330 assert_eq!(0, circuit.borrow().num_private());
331 assert_eq!(1, circuit.borrow().num_variables());
332 assert_eq!(0, circuit.borrow().num_constraints());
333 r1cs
335 })
336 }
337
338 fn eject_assignment_and_reset() -> Assignment<<Self::Network as console::Environment>::Field> {
340 CIRCUIT.with(|circuit| {
341 IN_WITNESS.with(|in_witness| in_witness.replace(false));
343 Self::set_variable_limit(None);
345 Self::set_constraint_limit(None);
347 let r1cs = circuit.replace(R1CS::<<Self as Environment>::BaseField>::new());
349 assert_eq!(0, circuit.borrow().num_constants());
350 assert_eq!(1, circuit.borrow().num_public());
351 assert_eq!(0, circuit.borrow().num_private());
352 assert_eq!(1, circuit.borrow().num_variables());
353 assert_eq!(0, circuit.borrow().num_constraints());
354 Assignment::from(r1cs)
356 })
357 }
358
359 fn reset() {
361 CIRCUIT.with(|circuit| {
362 IN_WITNESS.with(|in_witness| in_witness.replace(false));
364 Self::set_variable_limit(None);
366 Self::set_constraint_limit(None);
368 *circuit.borrow_mut() = R1CS::<<Self as Environment>::BaseField>::new();
370 assert_eq!(0, circuit.borrow().num_constants());
371 assert_eq!(1, circuit.borrow().num_public());
372 assert_eq!(0, circuit.borrow().num_private());
373 assert_eq!(1, circuit.borrow().num_variables());
374 assert_eq!(0, circuit.borrow().num_constraints());
375 });
376 }
377}
378
379impl fmt::Display for Circuit {
380 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
381 CIRCUIT.with(|circuit| write!(f, "{}", circuit.borrow()))
382 }
383}
384
385#[cfg(test)]
386mod tests {
387 use snarkvm_circuit::prelude::*;
388
389 fn create_example_circuit<E: Environment>() -> Field<E> {
391 let one = snarkvm_console_types::Field::<E::Network>::one();
392 let two = one + one;
393
394 const EXPONENT: u64 = 64;
395
396 let mut candidate = Field::<E>::new(Mode::Public, one);
398 let mut accumulator = Field::new(Mode::Private, two);
399 for _ in 0..EXPONENT {
400 candidate += &accumulator;
401 accumulator *= Field::new(Mode::Private, two);
402 }
403
404 assert_eq!((accumulator - Field::one()).eject_value(), candidate.eject_value());
405 assert_eq!(2, E::num_public());
406 assert_eq!(2 * EXPONENT + 1, E::num_private());
407 assert_eq!(EXPONENT, E::num_constraints());
408 assert!(E::is_satisfied());
409
410 candidate
411 }
412
413 #[test]
414 fn test_print_circuit() {
415 let _candidate = create_example_circuit::<Circuit>();
416 let output = format!("{Circuit}");
417 println!("{output}");
418 }
419
420 #[test]
421 fn test_circuit_scope() {
422 Circuit::scope("test_circuit_scope", || {
423 assert_eq!(0, Circuit::num_constants());
424 assert_eq!(1, Circuit::num_public());
425 assert_eq!(0, Circuit::num_private());
426 assert_eq!(0, Circuit::num_constraints());
427
428 assert_eq!(0, Circuit::num_constants_in_scope());
429 assert_eq!(0, Circuit::num_public_in_scope());
430 assert_eq!(0, Circuit::num_private_in_scope());
431 assert_eq!(0, Circuit::num_constraints_in_scope());
432 })
433 }
434}