#![forbid(unsafe_code)]
#![allow(clippy::too_many_arguments)]
#![cfg_attr(test, allow(clippy::assertions_on_result_states))]
mod helpers;
pub mod and;
pub mod equal;
pub mod nand;
pub mod nor;
pub mod not;
pub mod or;
pub mod ternary;
pub mod xor;
#[cfg(test)]
use snarkvm_circuit_environment::{assert_count, assert_output_mode, assert_scope, count, output_mode};
use snarkvm_circuit_environment::prelude::*;
use core::ops::Deref;
#[derive(Clone)]
pub struct Boolean<E: Environment>(LinearCombination<E::BaseField>);
impl<E: Environment> BooleanTrait for Boolean<E> {}
#[cfg(console)]
impl<E: Environment> Inject for Boolean<E> {
type Primitive = bool;
fn new(mode: Mode, value: Self::Primitive) -> Self {
let variable = E::new_variable(mode, match value {
true => E::BaseField::one(),
false => E::BaseField::zero(),
});
E::enforce(|| (E::one() - &variable, &variable, E::zero()));
Self(variable.into())
}
fn constant(value: Self::Primitive) -> Self {
match value {
true => Self(E::one()),
false => Self(E::zero()),
}
}
}
#[cfg(console)]
impl<E: Environment> Eject for Boolean<E> {
type Primitive = bool;
fn eject_mode(&self) -> Mode {
match self.0.is_boolean_type() {
true => self.0.mode(),
false => E::halt("Boolean variable is not well-formed"),
}
}
fn eject_value(&self) -> Self::Primitive {
let value = self.0.value();
debug_assert!(value.is_zero() || value.is_one());
value.is_one()
}
}
#[cfg(console)]
impl<E: Environment> Parser for Boolean<E> {
#[inline]
fn parse(string: &str) -> ParserResult<Self> {
let (string, boolean) = console::Boolean::<E::Network>::parse(string)?;
let (string, mode) = opt(pair(tag("."), Mode::parse))(string)?;
match mode {
Some((_, mode)) => Ok((string, Boolean::new(mode, *boolean))),
None => Ok((string, Boolean::new(Mode::Constant, *boolean))),
}
}
}
#[cfg(console)]
impl<E: Environment> FromStr for Boolean<E> {
type Err = Error;
#[inline]
fn from_str(string: &str) -> Result<Self> {
match Self::parse(string) {
Ok((remainder, object)) => {
ensure!(remainder.is_empty(), "Failed to parse string. Found invalid character in: \"{remainder}\"");
Ok(object)
}
Err(error) => bail!("Failed to parse string. {error}"),
}
}
}
#[cfg(console)]
impl<E: Environment> TypeName for Boolean<E> {
#[inline]
fn type_name() -> &'static str {
console::Boolean::<E::Network>::type_name()
}
}
#[cfg(console)]
impl<E: Environment> Debug for Boolean<E> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
Display::fmt(self, f)
}
}
#[cfg(console)]
impl<E: Environment> Display for Boolean<E> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}.{}", self.eject_value(), self.eject_mode())
}
}
impl<E: Environment> Deref for Boolean<E> {
type Target = LinearCombination<E::BaseField>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<E: Environment> From<Boolean<E>> for LinearCombination<E::BaseField> {
fn from(boolean: Boolean<E>) -> Self {
boolean.0
}
}
impl<E: Environment> From<&Boolean<E>> for LinearCombination<E::BaseField> {
fn from(boolean: &Boolean<E>) -> Self {
boolean.0.clone()
}
}
#[cfg(test)]
mod tests {
use super::*;
use snarkvm_circuit_environment::Circuit;
#[test]
fn test_new_constant() {
Circuit::scope("test_new_constant", || {
let candidate = Boolean::<Circuit>::new(Mode::Constant, false);
assert!(!candidate.eject_value()); assert_scope!(1, 0, 0, 0);
let candidate = Boolean::<Circuit>::new(Mode::Constant, true);
assert!(candidate.eject_value()); assert_scope!(2, 0, 0, 0);
});
}
#[test]
fn test_new_public() {
Circuit::scope("test_new_public", || {
let candidate = Boolean::<Circuit>::new(Mode::Public, false);
assert!(!candidate.eject_value()); assert_scope!(0, 1, 0, 1);
let candidate = Boolean::<Circuit>::new(Mode::Public, true);
assert!(candidate.eject_value()); assert_scope!(0, 2, 0, 2);
});
}
#[test]
fn test_new_private() {
Circuit::scope("test_new_private", || {
let candidate = Boolean::<Circuit>::new(Mode::Private, false);
assert!(!candidate.eject_value()); assert_scope!(0, 0, 1, 1);
let candidate = Boolean::<Circuit>::new(Mode::Private, true);
assert!(candidate.eject_value()); assert_scope!(0, 0, 2, 2);
});
}
#[test]
fn test_new_fail() {
let one = <Circuit as Environment>::BaseField::one();
let two = one + one;
{
let candidate = Circuit::new_variable(Mode::Constant, two);
assert!(
std::panic::catch_unwind(|| Circuit::enforce(|| (
Circuit::one() - &candidate,
candidate,
Circuit::zero()
)))
.is_err()
);
assert_eq!(0, Circuit::num_constraints());
Circuit::reset();
}
{
let candidate = Circuit::new_variable(Mode::Public, two);
Circuit::enforce(|| (Circuit::one() - &candidate, candidate, Circuit::zero()));
assert!(!Circuit::is_satisfied());
Circuit::reset();
}
{
let candidate = Circuit::new_variable(Mode::Private, two);
Circuit::enforce(|| (Circuit::one() - &candidate, candidate, Circuit::zero()));
assert!(!Circuit::is_satisfied());
Circuit::reset();
}
}
#[test]
fn test_parser() {
let (_, candidate) = Boolean::<Circuit>::parse("true").unwrap();
assert!(candidate.eject_value());
assert!(candidate.is_constant());
let (_, candidate) = Boolean::<Circuit>::parse("false").unwrap();
assert!(!candidate.eject_value());
assert!(candidate.is_constant());
let (_, candidate) = Boolean::<Circuit>::parse("true.constant").unwrap();
assert!(candidate.eject_value());
assert!(candidate.is_constant());
let (_, candidate) = Boolean::<Circuit>::parse("false.constant").unwrap();
assert!(!candidate.eject_value());
assert!(candidate.is_constant());
let (_, candidate) = Boolean::<Circuit>::parse("true.public").unwrap();
assert!(candidate.eject_value());
assert!(candidate.is_public());
let (_, candidate) = Boolean::<Circuit>::parse("false.public").unwrap();
assert!(!candidate.eject_value());
assert!(candidate.is_public());
let (_, candidate) = Boolean::<Circuit>::parse("true.private").unwrap();
assert!(candidate.eject_value());
assert!(candidate.is_private());
let (_, candidate) = Boolean::<Circuit>::parse("false.private").unwrap();
assert!(!candidate.eject_value());
assert!(candidate.is_private());
for mode in [Mode::Constant, Mode::Public, Mode::Private] {
for value in [true, false] {
let expected = Boolean::<Circuit>::new(mode, value);
let (_, candidate) = Boolean::<Circuit>::parse(&format!("{expected}")).unwrap();
assert_eq!(expected.eject_value(), candidate.eject_value());
assert_eq!(mode, candidate.eject_mode());
}
}
}
#[test]
fn test_display() {
let candidate = Boolean::<Circuit>::new(Mode::Constant, false);
assert_eq!("false.constant", format!("{candidate}"));
let candidate = Boolean::<Circuit>::new(Mode::Constant, true);
assert_eq!("true.constant", format!("{candidate}"));
let candidate = Boolean::<Circuit>::new(Mode::Public, false);
assert_eq!("false.public", format!("{candidate}"));
let candidate = Boolean::<Circuit>::new(Mode::Public, true);
assert_eq!("true.public", format!("{candidate}"));
let candidate = Boolean::<Circuit>::new(Mode::Private, false);
assert_eq!("false.private", format!("{candidate}"));
let candidate = Boolean::<Circuit>::new(Mode::Private, true);
assert_eq!("true.private", format!("{candidate}"));
}
}