snarkvm_circuit_environment/
canary_circuit.rs1use crate::{Mode, helpers::Constraint, *};
17
18use core::{
19 cell::{Cell, RefCell},
20 fmt,
21};
22
23type Field = <console::CanaryV0 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 CANARY_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 CanaryCircuit;
36
37impl Environment for CanaryCircuit {
38 type Affine = <console::CanaryV0 as console::Environment>::Affine;
39 type BaseField = Field;
40 type Network = console::CanaryV0;
41 type ScalarField = <console::CanaryV0 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 CANARY_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
95 where
96 Fn: FnOnce() -> Output,
97 {
98 IN_WITNESS.with(|in_witness| {
99 if !in_witness.get() {
101 CANARY_CIRCUIT.with(|circuit| {
102 let name = name.into();
104 if let Err(error) = circuit.borrow_mut().push_scope(&name) {
105 Self::halt(error)
106 }
107
108 let output = logic();
110
111 if let Err(error) = circuit.borrow_mut().pop_scope(name) {
113 Self::halt(error)
114 }
115
116 output
117 })
118 } else {
119 Self::halt("Tried to initialize a new scope in witness mode")
120 }
121 })
122 }
123
124 fn enforce<Fn, A, B, C>(constraint: Fn)
126 where
127 Fn: FnOnce() -> (A, B, C),
128 A: Into<LinearCombination<Self::BaseField>>,
129 B: Into<LinearCombination<Self::BaseField>>,
130 C: Into<LinearCombination<Self::BaseField>>,
131 {
132 IN_WITNESS.with(|in_witness| {
133 if !in_witness.get() {
135 CANARY_CIRCUIT.with(|circuit| {
136 CONSTRAINT_LIMIT.with(|constraint_limit| {
138 if let Some(limit) = constraint_limit.get() {
139 if circuit.borrow().num_constraints() > limit {
140 Self::halt(format!("Surpassed the constraint limit ({limit})"))
141 }
142 }
143 });
144
145 let (a, b, c) = constraint();
146 let (a, b, c) = (a.into(), b.into(), c.into());
147
148 match a.is_constant() && b.is_constant() && c.is_constant() {
150 true => {
151 assert_eq!(
153 a.value() * b.value(),
154 c.value(),
155 "Constant constraint failed: ({a} * {b}) =?= {c}"
156 );
157
158 }
166 false => {
167 let constraint = Constraint(circuit.borrow().scope(), a, b, c);
169 circuit.borrow_mut().enforce(constraint)
171 }
172 }
173 });
174 } else {
175 Self::halt("Tried to add a new constraint in witness mode")
176 }
177 })
178 }
179
180 fn is_satisfied() -> bool {
182 CANARY_CIRCUIT.with(|circuit| circuit.borrow().is_satisfied())
183 }
184
185 fn is_satisfied_in_scope() -> bool {
187 CANARY_CIRCUIT.with(|circuit| circuit.borrow().is_satisfied_in_scope())
188 }
189
190 fn num_constants() -> u64 {
192 CANARY_CIRCUIT.with(|circuit| circuit.borrow().num_constants())
193 }
194
195 fn num_public() -> u64 {
197 CANARY_CIRCUIT.with(|circuit| circuit.borrow().num_public())
198 }
199
200 fn num_private() -> u64 {
202 CANARY_CIRCUIT.with(|circuit| circuit.borrow().num_private())
203 }
204
205 fn num_variables() -> u64 {
207 CANARY_CIRCUIT.with(|circuit| circuit.borrow().num_variables())
208 }
209
210 fn num_constraints() -> u64 {
212 CANARY_CIRCUIT.with(|circuit| circuit.borrow().num_constraints())
213 }
214
215 fn num_nonzeros() -> (u64, u64, u64) {
217 CANARY_CIRCUIT.with(|circuit| circuit.borrow().num_nonzeros())
218 }
219
220 fn num_constants_in_scope() -> u64 {
222 CANARY_CIRCUIT.with(|circuit| circuit.borrow().num_constants_in_scope())
223 }
224
225 fn num_public_in_scope() -> u64 {
227 CANARY_CIRCUIT.with(|circuit| circuit.borrow().num_public_in_scope())
228 }
229
230 fn num_private_in_scope() -> u64 {
232 CANARY_CIRCUIT.with(|circuit| circuit.borrow().num_private_in_scope())
233 }
234
235 fn num_constraints_in_scope() -> u64 {
237 CANARY_CIRCUIT.with(|circuit| circuit.borrow().num_constraints_in_scope())
238 }
239
240 fn num_nonzeros_in_scope() -> (u64, u64, u64) {
242 CANARY_CIRCUIT.with(|circuit| circuit.borrow().num_nonzeros_in_scope())
243 }
244
245 fn get_variable_limit() -> Option<u64> {
247 VARIABLE_LIMIT.with(|current_limit| current_limit.get())
248 }
249
250 fn set_variable_limit(limit: Option<u64>) {
252 VARIABLE_LIMIT.with(|current_limit| current_limit.replace(limit));
253 }
254
255 fn get_constraint_limit() -> Option<u64> {
257 CONSTRAINT_LIMIT.with(|current_limit| current_limit.get())
258 }
259
260 fn set_constraint_limit(limit: Option<u64>) {
262 CONSTRAINT_LIMIT.with(|current_limit| current_limit.replace(limit));
263 }
264
265 fn halt<S: Into<String>, T>(message: S) -> T {
267 let error = message.into();
268 panic!("{}", &error)
270 }
271
272 fn inject_r1cs(r1cs: R1CS<Self::BaseField>) {
274 CANARY_CIRCUIT.with(|circuit| {
275 assert_eq!(0, circuit.borrow().num_constants());
277 assert_eq!(1, circuit.borrow().num_public());
278 assert_eq!(0, circuit.borrow().num_private());
279 assert_eq!(1, circuit.borrow().num_variables());
280 assert_eq!(0, circuit.borrow().num_constraints());
281 let r1cs = circuit.replace(r1cs);
283 assert_eq!(0, r1cs.num_constants());
285 assert_eq!(1, r1cs.num_public());
286 assert_eq!(0, r1cs.num_private());
287 assert_eq!(1, r1cs.num_variables());
288 assert_eq!(0, r1cs.num_constraints());
289 })
290 }
291
292 fn eject_r1cs_and_reset() -> R1CS<Self::BaseField> {
294 CANARY_CIRCUIT.with(|circuit| {
295 IN_WITNESS.with(|in_witness| in_witness.replace(false));
297 Self::set_variable_limit(None);
299 Self::set_constraint_limit(None);
301 let r1cs = circuit.replace(R1CS::<<Self as Environment>::BaseField>::new());
303 assert_eq!(0, circuit.borrow().num_constants());
305 assert_eq!(1, circuit.borrow().num_public());
306 assert_eq!(0, circuit.borrow().num_private());
307 assert_eq!(1, circuit.borrow().num_variables());
308 assert_eq!(0, circuit.borrow().num_constraints());
309 r1cs
311 })
312 }
313
314 fn eject_assignment_and_reset() -> Assignment<<Self::Network as console::Environment>::Field> {
316 CANARY_CIRCUIT.with(|circuit| {
317 IN_WITNESS.with(|in_witness| in_witness.replace(false));
319 Self::set_variable_limit(None);
321 Self::set_constraint_limit(None);
323 let r1cs = circuit.replace(R1CS::<<Self as Environment>::BaseField>::new());
325 assert_eq!(0, circuit.borrow().num_constants());
326 assert_eq!(1, circuit.borrow().num_public());
327 assert_eq!(0, circuit.borrow().num_private());
328 assert_eq!(1, circuit.borrow().num_variables());
329 assert_eq!(0, circuit.borrow().num_constraints());
330 Assignment::from(r1cs)
332 })
333 }
334
335 fn reset() {
337 CANARY_CIRCUIT.with(|circuit| {
338 IN_WITNESS.with(|in_witness| in_witness.replace(false));
340 Self::set_variable_limit(None);
342 Self::set_constraint_limit(None);
344 *circuit.borrow_mut() = R1CS::<<Self as Environment>::BaseField>::new();
346 assert_eq!(0, circuit.borrow().num_constants());
347 assert_eq!(1, circuit.borrow().num_public());
348 assert_eq!(0, circuit.borrow().num_private());
349 assert_eq!(1, circuit.borrow().num_variables());
350 assert_eq!(0, circuit.borrow().num_constraints());
351 });
352 }
353}
354
355impl fmt::Display for CanaryCircuit {
356 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
357 CANARY_CIRCUIT.with(|circuit| write!(f, "{}", circuit.borrow()))
358 }
359}
360
361#[cfg(test)]
362mod tests {
363 use snarkvm_circuit::prelude::*;
364
365 fn create_example_circuit<E: Environment>() -> Field<E> {
367 let one = snarkvm_console_types::Field::<E::Network>::one();
368 let two = one + one;
369
370 const EXPONENT: u64 = 64;
371
372 let mut candidate = Field::<E>::new(Mode::Public, one);
374 let mut accumulator = Field::new(Mode::Private, two);
375 for _ in 0..EXPONENT {
376 candidate += &accumulator;
377 accumulator *= Field::new(Mode::Private, two);
378 }
379
380 assert_eq!((accumulator - Field::one()).eject_value(), candidate.eject_value());
381 assert_eq!(2, E::num_public());
382 assert_eq!(2 * EXPONENT + 1, E::num_private());
383 assert_eq!(EXPONENT, E::num_constraints());
384 assert!(E::is_satisfied());
385
386 candidate
387 }
388
389 #[test]
390 fn test_print_circuit() {
391 let _candidate = create_example_circuit::<CanaryCircuit>();
392 let output = format!("{CanaryCircuit}");
393 println!("{output}");
394 }
395
396 #[test]
397 fn test_circuit_scope() {
398 CanaryCircuit::scope("test_circuit_scope", || {
399 assert_eq!(0, CanaryCircuit::num_constants());
400 assert_eq!(1, CanaryCircuit::num_public());
401 assert_eq!(0, CanaryCircuit::num_private());
402 assert_eq!(0, CanaryCircuit::num_constraints());
403
404 assert_eq!(0, CanaryCircuit::num_constants_in_scope());
405 assert_eq!(0, CanaryCircuit::num_public_in_scope());
406 assert_eq!(0, CanaryCircuit::num_private_in_scope());
407 assert_eq!(0, CanaryCircuit::num_constraints_in_scope());
408 })
409 }
410}