support conditional console functions

This commit is contained in:
collin 2020-08-16 16:10:07 -07:00
parent b2866951f6
commit 4533915438
18 changed files with 52 additions and 23 deletions

View File

@ -1,6 +1,6 @@
//! Enforces an assert equals statement in a compiled Leo program.
use crate::{errors::ConsoleError, evaluate_eq, program::ConstrainedProgram, value::ConstrainedValue, GroupType};
use crate::{errors::ConsoleError, program::ConstrainedProgram, value::ConstrainedValue, GroupType};
use leo_typed::{Expression, Span, Type};
use snarkos_models::{
@ -14,6 +14,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
cs: &mut CS,
file_scope: String,
function_scope: String,
indicator: Option<Boolean>,
expression: Expression,
span: Span,
) -> Result<(), ConsoleError> {
@ -23,21 +24,27 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
// Evaluate assert expression
let assert_expression = self.enforce_expression(cs, file_scope, function_scope, expected_type, expression)?;
// Expect assert expression to evaluate to true
let expect_true = ConstrainedValue::Boolean(Boolean::Constant(true));
let result = evaluate_eq(cs, expect_true, assert_expression, span.clone())?;
// If the indicator bit is false, do not evaluate the assertion
// This is okay since we are not enforcing any constraints
let false_boolean = Boolean::Constant(false);
if let Some(indicator_bool) = indicator {
if indicator_bool.eq(&false_boolean) {
return Ok(()); // continue execution
}
}
// Unwrap assertion value and handle errors
let result_option = match result {
let result_option = match assert_expression {
ConstrainedValue::Boolean(boolean) => boolean.get_value(),
_ => unreachable!("evaluate_eq must return boolean"),
_ => return Err(ConsoleError::assertion_must_be_boolean(expression_string, span.clone())),
};
let result_bool = result_option.ok_or(ConsoleError::assertion_depends_on_input(span.clone()))?;
if !result_bool {
Err(ConsoleError::assertion_failed(expression_string, span))
} else {
Ok(())
return Err(ConsoleError::assertion_failed(expression_string, span));
}
Ok(())
}
}

View File

@ -5,7 +5,7 @@ use leo_typed::{ConsoleFunction, ConsoleFunctionCall};
use snarkos_models::{
curves::{Field, PrimeField},
gadgets::r1cs::ConstraintSystem,
gadgets::{r1cs::ConstraintSystem, utilities::boolean::Boolean},
};
impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
@ -14,29 +14,49 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
cs: &mut CS,
file_scope: String,
function_scope: String,
indicator: Option<Boolean>,
console: ConsoleFunctionCall,
) -> Result<(), ConsoleError> {
match console.function {
ConsoleFunction::Assert(expression) => {
self.evaluate_console_assert(cs, file_scope, function_scope, expression, console.span)?;
self.evaluate_console_assert(cs, file_scope, function_scope, indicator, expression, console.span)?;
}
ConsoleFunction::Debug(string) => {
let string = self.format(cs, file_scope, function_scope, string)?;
log::debug!("{}", string);
if unwrap_indicator_value(indicator) {
log::debug!("{}", string);
}
}
ConsoleFunction::Error(string) => {
let string = self.format(cs, file_scope, function_scope, string)?;
log::error!("{}", string);
if unwrap_indicator_value(indicator) {
log::error!("{}", string);
}
}
ConsoleFunction::Log(string) => {
let string = self.format(cs, file_scope, function_scope, string)?;
println!("{}", string);
if unwrap_indicator_value(indicator) {
log::info!("{}", string);
}
}
}
Ok(())
}
}
// Return the indicator boolean gadget value or true if it is None
// This is okay since we are not enforcing any constraints
fn unwrap_indicator_value(indicator: Option<Boolean>) -> bool {
let false_boolean = Boolean::constant(false);
if let Some(indicator_bool) = indicator {
if indicator_bool.eq(&false_boolean) {
return false;
}
}
true
}

View File

@ -40,7 +40,13 @@ impl ConsoleError {
}
pub fn assertion_failed(expression: String, span: Span) -> Self {
let message = format!("Assertion `true == {}` failed", expression);
let message = format!("Assertion `{}` failed", expression);
Self::new_from_span(message, span)
}
pub fn assertion_must_be_boolean(expression: String, span: Span) -> Self {
let message = format!("Assertion expression `{}` must evaluate to a boolean value", expression);
Self::new_from_span(message, span)
}

View File

@ -11,7 +11,7 @@ use leo_typed::{Expression, Function, InputVariable, Span, Type};
use snarkos_models::{
curves::{Field, PrimeField},
gadgets::r1cs::ConstraintSystem,
gadgets::{r1cs::ConstraintSystem, utilities::boolean::Boolean},
};
pub fn check_arguments_length(expected: usize, actual: usize, span: Span) -> Result<(), FunctionError> {
@ -78,6 +78,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
// Evaluate every statement in the function and save all potential results
let mut results = vec![];
let indicator = Some(Boolean::constant(true));
for statement in function.statements.iter() {
let mut result = self.enforce_statement(

View File

@ -1,5 +0,0 @@
pub mod format;
pub use self::format::*;
pub mod macro_;
pub use self::macro_::*;

View File

@ -78,7 +78,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
results.append(&mut result);
}
Statement::Console(console) => {
self.evaluate_console_function_call(cs, file_scope, function_scope, console)?;
self.evaluate_console_function_call(cs, file_scope, function_scope, indicator, console)?;
}
Statement::Expression(expression, span) => {
let expression_string = expression.to_string();

View File

@ -2,6 +2,7 @@ pub mod address;
pub mod array;
pub mod boolean;
pub mod circuits;
pub mod console;
pub mod definition;
pub mod field;
pub mod function;
@ -9,7 +10,6 @@ pub mod group;
pub mod import;
pub mod input_files;
pub mod integers;
pub mod macros;
pub mod mutability;
pub mod statements;
pub mod syntax;