snarkvm_circuit_types_boolean/
lib.rs

1// Copyright (c) 2019-2025 Provable Inc.
2// This file is part of the snarkVM library.
3
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at:
7
8// http://www.apache.org/licenses/LICENSE-2.0
9
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15
16#![forbid(unsafe_code)]
17#![allow(clippy::too_many_arguments)]
18#![cfg_attr(test, allow(clippy::assertions_on_result_states))]
19
20extern crate snarkvm_console_types_boolean as console;
21
22mod helpers;
23
24pub mod and;
25pub mod equal;
26pub mod nand;
27pub mod nor;
28pub mod not;
29pub mod or;
30pub mod ternary;
31pub mod xor;
32
33#[cfg(test)]
34use snarkvm_circuit_environment::{assert_count, assert_output_mode, assert_scope, count, output_mode};
35
36use snarkvm_circuit_environment::prelude::*;
37
38use core::ops::Deref;
39
40#[derive(Clone)]
41pub struct Boolean<E: Environment>(LinearCombination<E::BaseField>);
42
43impl<E: Environment> BooleanTrait for Boolean<E> {}
44
45impl<E: Environment> Boolean<E> {
46    /// Initializes a boolean from a variable.
47    ///
48    /// Note: This method does **not** check the booleanity of the variable.
49    pub fn from_variable(var: Variable<E::BaseField>) -> Self {
50        // In debug-mode only, sanity check the booleanity of the variable.
51        debug_assert!(var.value().is_zero() || var.value().is_one(), "Boolean variable is not well-formed");
52        // Return the boolean.
53        Boolean(var.into())
54    }
55}
56
57impl<E: Environment> Inject for Boolean<E> {
58    type Primitive = bool;
59
60    /// Initializes a new instance of a boolean from a primitive boolean value.
61    fn new(mode: Mode, value: Self::Primitive) -> Self {
62        let variable = E::new_variable(mode, match value {
63            true => E::BaseField::one(),
64            false => E::BaseField::zero(),
65        });
66
67        // Ensure (1 - a) * a = 0
68        // `a` must be either 0 or 1.
69        E::enforce(|| (E::one() - &variable, &variable, E::zero()));
70
71        Self(variable.into())
72    }
73
74    /// Initializes a constant boolean circuit from a primitive boolean value.
75    fn constant(value: Self::Primitive) -> Self {
76        match value {
77            true => Self(E::one()),
78            false => Self(E::zero()),
79        }
80    }
81}
82
83impl<E: Environment> Eject for Boolean<E> {
84    type Primitive = bool;
85
86    /// Ejects the mode of the boolean.
87    fn eject_mode(&self) -> Mode {
88        // Perform a software-level safety check that the boolean is well-formed.
89        match self.0.is_boolean_type() {
90            true => self.0.mode(),
91            false => E::halt("Boolean variable is not well-formed"),
92        }
93    }
94
95    /// Ejects the boolean as a constant boolean value.
96    fn eject_value(&self) -> Self::Primitive {
97        let value = self.0.value();
98        debug_assert!(value.is_zero() || value.is_one());
99        value.is_one()
100    }
101}
102
103impl<E: Environment> Parser for Boolean<E> {
104    /// Parses a string into a boolean circuit.
105    #[inline]
106    fn parse(string: &str) -> ParserResult<Self> {
107        // Parse the boolean from the string.
108        let (string, boolean) = console::Boolean::<E::Network>::parse(string)?;
109        // Parse the mode from the string.
110        let (string, mode) = opt(pair(tag("."), Mode::parse))(string)?;
111
112        match mode {
113            Some((_, mode)) => Ok((string, Boolean::new(mode, *boolean))),
114            None => Ok((string, Boolean::new(Mode::Constant, *boolean))),
115        }
116    }
117}
118
119impl<E: Environment> FromStr for Boolean<E> {
120    type Err = Error;
121
122    /// Parses a string into a boolean.
123    #[inline]
124    fn from_str(string: &str) -> Result<Self> {
125        match Self::parse(string) {
126            Ok((remainder, object)) => {
127                // Ensure the remainder is empty.
128                ensure!(remainder.is_empty(), "Failed to parse string. Found invalid character in: \"{remainder}\"");
129                // Return the object.
130                Ok(object)
131            }
132            Err(error) => bail!("Failed to parse string. {error}"),
133        }
134    }
135}
136
137impl<E: Environment> TypeName for Boolean<E> {
138    /// Returns the type name of the circuit as a string.
139    #[inline]
140    fn type_name() -> &'static str {
141        console::Boolean::<E::Network>::type_name()
142    }
143}
144
145impl<E: Environment> Debug for Boolean<E> {
146    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
147        Display::fmt(self, f)
148    }
149}
150
151impl<E: Environment> Display for Boolean<E> {
152    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
153        write!(f, "{}.{}", self.eject_value(), self.eject_mode())
154    }
155}
156
157impl<E: Environment> Deref for Boolean<E> {
158    type Target = LinearCombination<E::BaseField>;
159
160    fn deref(&self) -> &Self::Target {
161        &self.0
162    }
163}
164
165impl<E: Environment> From<Boolean<E>> for LinearCombination<E::BaseField> {
166    fn from(boolean: Boolean<E>) -> Self {
167        boolean.0
168    }
169}
170
171impl<E: Environment> From<&Boolean<E>> for LinearCombination<E::BaseField> {
172    fn from(boolean: &Boolean<E>) -> Self {
173        boolean.0.clone()
174    }
175}
176
177#[cfg(test)]
178mod tests {
179    use super::*;
180    use snarkvm_circuit_environment::Circuit;
181
182    #[test]
183    fn test_new_constant() {
184        Circuit::scope("test_new_constant", || {
185            let candidate = Boolean::<Circuit>::new(Mode::Constant, false);
186            assert!(!candidate.eject_value()); // false
187            assert_scope!(1, 0, 0, 0);
188
189            let candidate = Boolean::<Circuit>::new(Mode::Constant, true);
190            assert!(candidate.eject_value()); // true
191            assert_scope!(2, 0, 0, 0);
192        });
193    }
194
195    #[test]
196    fn test_new_public() {
197        Circuit::scope("test_new_public", || {
198            let candidate = Boolean::<Circuit>::new(Mode::Public, false);
199            assert!(!candidate.eject_value()); // false
200            assert_scope!(0, 1, 0, 1);
201
202            let candidate = Boolean::<Circuit>::new(Mode::Public, true);
203            assert!(candidate.eject_value()); // true
204            assert_scope!(0, 2, 0, 2);
205        });
206    }
207
208    #[test]
209    fn test_new_private() {
210        Circuit::scope("test_new_private", || {
211            let candidate = Boolean::<Circuit>::new(Mode::Private, false);
212            assert!(!candidate.eject_value()); // false
213            assert_scope!(0, 0, 1, 1);
214
215            let candidate = Boolean::<Circuit>::new(Mode::Private, true);
216            assert!(candidate.eject_value()); // true
217            assert_scope!(0, 0, 2, 2);
218        });
219    }
220
221    #[test]
222    fn test_new_fail() {
223        let one = <Circuit as Environment>::BaseField::one();
224        let two = one + one;
225        {
226            let candidate = Circuit::new_variable(Mode::Constant, two);
227
228            // Ensure `a` is either 0 or 1:
229            // (1 - a) * a = 0
230            assert!(
231                std::panic::catch_unwind(|| Circuit::enforce(|| (
232                    Circuit::one() - &candidate,
233                    candidate,
234                    Circuit::zero()
235                )))
236                .is_err()
237            );
238            assert_eq!(0, Circuit::num_constraints());
239
240            Circuit::reset();
241        }
242        {
243            let candidate = Circuit::new_variable(Mode::Public, two);
244
245            // Ensure `a` is either 0 or 1:
246            // (1 - a) * a = 0
247            Circuit::enforce(|| (Circuit::one() - &candidate, candidate, Circuit::zero()));
248            assert!(!Circuit::is_satisfied());
249
250            Circuit::reset();
251        }
252        {
253            let candidate = Circuit::new_variable(Mode::Private, two);
254
255            // Ensure `a` is either 0 or 1:
256            // (1 - a) * a = 0
257            Circuit::enforce(|| (Circuit::one() - &candidate, candidate, Circuit::zero()));
258            assert!(!Circuit::is_satisfied());
259
260            Circuit::reset();
261        }
262    }
263
264    #[test]
265    fn test_parser() {
266        let (_, candidate) = Boolean::<Circuit>::parse("true").unwrap();
267        assert!(candidate.eject_value());
268        assert!(candidate.is_constant());
269
270        let (_, candidate) = Boolean::<Circuit>::parse("false").unwrap();
271        assert!(!candidate.eject_value());
272        assert!(candidate.is_constant());
273
274        let (_, candidate) = Boolean::<Circuit>::parse("true.constant").unwrap();
275        assert!(candidate.eject_value());
276        assert!(candidate.is_constant());
277
278        let (_, candidate) = Boolean::<Circuit>::parse("false.constant").unwrap();
279        assert!(!candidate.eject_value());
280        assert!(candidate.is_constant());
281
282        let (_, candidate) = Boolean::<Circuit>::parse("true.public").unwrap();
283        assert!(candidate.eject_value());
284        assert!(candidate.is_public());
285
286        let (_, candidate) = Boolean::<Circuit>::parse("false.public").unwrap();
287        assert!(!candidate.eject_value());
288        assert!(candidate.is_public());
289
290        let (_, candidate) = Boolean::<Circuit>::parse("true.private").unwrap();
291        assert!(candidate.eject_value());
292        assert!(candidate.is_private());
293
294        let (_, candidate) = Boolean::<Circuit>::parse("false.private").unwrap();
295        assert!(!candidate.eject_value());
296        assert!(candidate.is_private());
297
298        for mode in [Mode::Constant, Mode::Public, Mode::Private] {
299            for value in [true, false] {
300                let expected = Boolean::<Circuit>::new(mode, value);
301
302                let (_, candidate) = Boolean::<Circuit>::parse(&format!("{expected}")).unwrap();
303                assert_eq!(expected.eject_value(), candidate.eject_value());
304                assert_eq!(mode, candidate.eject_mode());
305            }
306        }
307    }
308
309    #[test]
310    fn test_display() {
311        let candidate = Boolean::<Circuit>::new(Mode::Constant, false);
312        assert_eq!("false.constant", format!("{candidate}"));
313
314        let candidate = Boolean::<Circuit>::new(Mode::Constant, true);
315        assert_eq!("true.constant", format!("{candidate}"));
316
317        let candidate = Boolean::<Circuit>::new(Mode::Public, false);
318        assert_eq!("false.public", format!("{candidate}"));
319
320        let candidate = Boolean::<Circuit>::new(Mode::Public, true);
321        assert_eq!("true.public", format!("{candidate}"));
322
323        let candidate = Boolean::<Circuit>::new(Mode::Private, false);
324        assert_eq!("false.private", format!("{candidate}"));
325
326        let candidate = Boolean::<Circuit>::new(Mode::Private, true);
327        assert_eq!("true.private", format!("{candidate}"));
328    }
329}