snarkvm_circuit_types_boolean/
lib.rs

1// Copyright 2024 Aleo Network Foundation
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
20mod helpers;
21
22pub mod and;
23pub mod equal;
24pub mod nand;
25pub mod nor;
26pub mod not;
27pub mod or;
28pub mod ternary;
29pub mod xor;
30
31#[cfg(test)]
32use snarkvm_circuit_environment::{assert_count, assert_output_mode, assert_scope, count, output_mode};
33
34use snarkvm_circuit_environment::prelude::*;
35
36use core::ops::Deref;
37
38#[derive(Clone)]
39pub struct Boolean<E: Environment>(LinearCombination<E::BaseField>);
40
41impl<E: Environment> BooleanTrait for Boolean<E> {}
42
43impl<E: Environment> Boolean<E> {
44    /// Initializes a boolean from a variable.
45    ///
46    /// Note: This method does **not** check the booleanity of the variable.
47    pub fn from_variable(var: Variable<E::BaseField>) -> Self {
48        // In debug-mode only, sanity check the booleanity of the variable.
49        debug_assert!(var.value().is_zero() || var.value().is_one(), "Boolean variable is not well-formed");
50        // Return the boolean.
51        Boolean(var.into())
52    }
53}
54
55#[cfg(feature = "console")]
56impl<E: Environment> Inject for Boolean<E> {
57    type Primitive = bool;
58
59    /// Initializes a new instance of a boolean from a primitive boolean value.
60    fn new(mode: Mode, value: Self::Primitive) -> Self {
61        let variable = E::new_variable(mode, match value {
62            true => E::BaseField::one(),
63            false => E::BaseField::zero(),
64        });
65
66        // Ensure (1 - a) * a = 0
67        // `a` must be either 0 or 1.
68        E::enforce(|| (E::one() - &variable, &variable, E::zero()));
69
70        Self(variable.into())
71    }
72
73    /// Initializes a constant boolean circuit from a primitive boolean value.
74    fn constant(value: Self::Primitive) -> Self {
75        match value {
76            true => Self(E::one()),
77            false => Self(E::zero()),
78        }
79    }
80}
81
82#[cfg(feature = "console")]
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
103#[cfg(feature = "console")]
104impl<E: Environment> Parser for Boolean<E> {
105    /// Parses a string into a boolean circuit.
106    #[inline]
107    fn parse(string: &str) -> ParserResult<Self> {
108        // Parse the boolean from the string.
109        let (string, boolean) = console::Boolean::<E::Network>::parse(string)?;
110        // Parse the mode from the string.
111        let (string, mode) = opt(pair(tag("."), Mode::parse))(string)?;
112
113        match mode {
114            Some((_, mode)) => Ok((string, Boolean::new(mode, *boolean))),
115            None => Ok((string, Boolean::new(Mode::Constant, *boolean))),
116        }
117    }
118}
119
120#[cfg(feature = "console")]
121impl<E: Environment> FromStr for Boolean<E> {
122    type Err = Error;
123
124    /// Parses a string into a boolean.
125    #[inline]
126    fn from_str(string: &str) -> Result<Self> {
127        match Self::parse(string) {
128            Ok((remainder, object)) => {
129                // Ensure the remainder is empty.
130                ensure!(remainder.is_empty(), "Failed to parse string. Found invalid character in: \"{remainder}\"");
131                // Return the object.
132                Ok(object)
133            }
134            Err(error) => bail!("Failed to parse string. {error}"),
135        }
136    }
137}
138
139#[cfg(feature = "console")]
140impl<E: Environment> TypeName for Boolean<E> {
141    /// Returns the type name of the circuit as a string.
142    #[inline]
143    fn type_name() -> &'static str {
144        console::Boolean::<E::Network>::type_name()
145    }
146}
147
148#[cfg(feature = "console")]
149impl<E: Environment> Debug for Boolean<E> {
150    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
151        Display::fmt(self, f)
152    }
153}
154
155#[cfg(feature = "console")]
156impl<E: Environment> Display for Boolean<E> {
157    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
158        write!(f, "{}.{}", self.eject_value(), self.eject_mode())
159    }
160}
161
162impl<E: Environment> Deref for Boolean<E> {
163    type Target = LinearCombination<E::BaseField>;
164
165    fn deref(&self) -> &Self::Target {
166        &self.0
167    }
168}
169
170impl<E: Environment> From<Boolean<E>> for LinearCombination<E::BaseField> {
171    fn from(boolean: Boolean<E>) -> Self {
172        boolean.0
173    }
174}
175
176impl<E: Environment> From<&Boolean<E>> for LinearCombination<E::BaseField> {
177    fn from(boolean: &Boolean<E>) -> Self {
178        boolean.0.clone()
179    }
180}
181
182#[cfg(test)]
183mod tests {
184    use super::*;
185    use snarkvm_circuit_environment::Circuit;
186
187    #[test]
188    fn test_new_constant() {
189        Circuit::scope("test_new_constant", || {
190            let candidate = Boolean::<Circuit>::new(Mode::Constant, false);
191            assert!(!candidate.eject_value()); // false
192            assert_scope!(1, 0, 0, 0);
193
194            let candidate = Boolean::<Circuit>::new(Mode::Constant, true);
195            assert!(candidate.eject_value()); // true
196            assert_scope!(2, 0, 0, 0);
197        });
198    }
199
200    #[test]
201    fn test_new_public() {
202        Circuit::scope("test_new_public", || {
203            let candidate = Boolean::<Circuit>::new(Mode::Public, false);
204            assert!(!candidate.eject_value()); // false
205            assert_scope!(0, 1, 0, 1);
206
207            let candidate = Boolean::<Circuit>::new(Mode::Public, true);
208            assert!(candidate.eject_value()); // true
209            assert_scope!(0, 2, 0, 2);
210        });
211    }
212
213    #[test]
214    fn test_new_private() {
215        Circuit::scope("test_new_private", || {
216            let candidate = Boolean::<Circuit>::new(Mode::Private, false);
217            assert!(!candidate.eject_value()); // false
218            assert_scope!(0, 0, 1, 1);
219
220            let candidate = Boolean::<Circuit>::new(Mode::Private, true);
221            assert!(candidate.eject_value()); // true
222            assert_scope!(0, 0, 2, 2);
223        });
224    }
225
226    #[test]
227    fn test_new_fail() {
228        let one = <Circuit as Environment>::BaseField::one();
229        let two = one + one;
230        {
231            let candidate = Circuit::new_variable(Mode::Constant, two);
232
233            // Ensure `a` is either 0 or 1:
234            // (1 - a) * a = 0
235            assert!(
236                std::panic::catch_unwind(|| Circuit::enforce(|| (
237                    Circuit::one() - &candidate,
238                    candidate,
239                    Circuit::zero()
240                )))
241                .is_err()
242            );
243            assert_eq!(0, Circuit::num_constraints());
244
245            Circuit::reset();
246        }
247        {
248            let candidate = Circuit::new_variable(Mode::Public, two);
249
250            // Ensure `a` is either 0 or 1:
251            // (1 - a) * a = 0
252            Circuit::enforce(|| (Circuit::one() - &candidate, candidate, Circuit::zero()));
253            assert!(!Circuit::is_satisfied());
254
255            Circuit::reset();
256        }
257        {
258            let candidate = Circuit::new_variable(Mode::Private, two);
259
260            // Ensure `a` is either 0 or 1:
261            // (1 - a) * a = 0
262            Circuit::enforce(|| (Circuit::one() - &candidate, candidate, Circuit::zero()));
263            assert!(!Circuit::is_satisfied());
264
265            Circuit::reset();
266        }
267    }
268
269    #[test]
270    fn test_parser() {
271        let (_, candidate) = Boolean::<Circuit>::parse("true").unwrap();
272        assert!(candidate.eject_value());
273        assert!(candidate.is_constant());
274
275        let (_, candidate) = Boolean::<Circuit>::parse("false").unwrap();
276        assert!(!candidate.eject_value());
277        assert!(candidate.is_constant());
278
279        let (_, candidate) = Boolean::<Circuit>::parse("true.constant").unwrap();
280        assert!(candidate.eject_value());
281        assert!(candidate.is_constant());
282
283        let (_, candidate) = Boolean::<Circuit>::parse("false.constant").unwrap();
284        assert!(!candidate.eject_value());
285        assert!(candidate.is_constant());
286
287        let (_, candidate) = Boolean::<Circuit>::parse("true.public").unwrap();
288        assert!(candidate.eject_value());
289        assert!(candidate.is_public());
290
291        let (_, candidate) = Boolean::<Circuit>::parse("false.public").unwrap();
292        assert!(!candidate.eject_value());
293        assert!(candidate.is_public());
294
295        let (_, candidate) = Boolean::<Circuit>::parse("true.private").unwrap();
296        assert!(candidate.eject_value());
297        assert!(candidate.is_private());
298
299        let (_, candidate) = Boolean::<Circuit>::parse("false.private").unwrap();
300        assert!(!candidate.eject_value());
301        assert!(candidate.is_private());
302
303        for mode in [Mode::Constant, Mode::Public, Mode::Private] {
304            for value in [true, false] {
305                let expected = Boolean::<Circuit>::new(mode, value);
306
307                let (_, candidate) = Boolean::<Circuit>::parse(&format!("{expected}")).unwrap();
308                assert_eq!(expected.eject_value(), candidate.eject_value());
309                assert_eq!(mode, candidate.eject_mode());
310            }
311        }
312    }
313
314    #[test]
315    fn test_display() {
316        let candidate = Boolean::<Circuit>::new(Mode::Constant, false);
317        assert_eq!("false.constant", format!("{candidate}"));
318
319        let candidate = Boolean::<Circuit>::new(Mode::Constant, true);
320        assert_eq!("true.constant", format!("{candidate}"));
321
322        let candidate = Boolean::<Circuit>::new(Mode::Public, false);
323        assert_eq!("false.public", format!("{candidate}"));
324
325        let candidate = Boolean::<Circuit>::new(Mode::Public, true);
326        assert_eq!("true.public", format!("{candidate}"));
327
328        let candidate = Boolean::<Circuit>::new(Mode::Private, false);
329        assert_eq!("false.private", format!("{candidate}"));
330
331        let candidate = Boolean::<Circuit>::new(Mode::Private, true);
332        assert_eq!("true.private", format!("{candidate}"));
333    }
334}