Merge pull request #55 from AleoHQ/refactor/conditional

Refactor conditional statements
This commit is contained in:
Howard Wu 2020-06-16 00:25:58 -07:00 committed by GitHub
commit b0773e0ca8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 632 additions and 142 deletions

View File

@ -12,7 +12,7 @@ use snarkos_models::{
curves::{Field, PrimeField},
gadgets::{
r1cs::ConstraintSystem,
utilities::{alloc::AllocGadget, boolean::Boolean, eq::EqGadget},
utilities::{alloc::AllocGadget, boolean::Boolean},
},
};
@ -46,10 +46,6 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
Ok(ConstrainedValue::Boolean(number))
}
pub(crate) fn get_boolean_constant(bool: Boolean) -> ConstrainedValue<F, G> {
ConstrainedValue::Boolean(bool)
}
pub(crate) fn evaluate_not(value: ConstrainedValue<F, G>) -> Result<ConstrainedValue<F, G>, BooleanError> {
match value {
ConstrainedValue::Boolean(boolean) => Ok(ConstrainedValue::Boolean(boolean.not())),
@ -90,17 +86,4 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
))),
}
}
pub(crate) fn boolean_eq(left: Boolean, right: Boolean) -> ConstrainedValue<F, G> {
ConstrainedValue::Boolean(Boolean::Constant(left.eq(&right)))
}
pub(crate) fn enforce_boolean_eq<CS: ConstraintSystem<F>>(
&mut self,
cs: &mut CS,
left: Boolean,
right: Boolean,
) -> Result<(), BooleanError> {
Ok(left.enforce_equal(cs.ns(|| format!("enforce bool equal")), &right)?)
}
}

View File

@ -23,7 +23,7 @@ use snarkos_models::{
curves::{Field, PrimeField},
gadgets::{
r1cs::ConstraintSystem,
utilities::{boolean::Boolean, select::CondSelectGadget},
utilities::{boolean::Boolean, eq::EvaluateEqGadget, select::CondSelectGadget},
},
};
@ -185,36 +185,41 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
}
/// Evaluate Boolean operations
fn evaluate_eq_expression(
fn evaluate_eq_expression<CS: ConstraintSystem<F>>(
&mut self,
cs: &mut CS,
left: ConstrainedValue<F, G>,
right: ConstrainedValue<F, G>,
) -> Result<ConstrainedValue<F, G>, ExpressionError> {
match (left, right) {
let mut expression_namespace = cs.ns(|| format!("evaluate {} == {}", left.to_string(), right.to_string()));
let result_bool = match (left, right) {
(ConstrainedValue::Boolean(bool_1), ConstrainedValue::Boolean(bool_2)) => {
Ok(Self::boolean_eq(bool_1, bool_2))
bool_1.evaluate_equal(expression_namespace, &bool_2)?
}
(ConstrainedValue::Integer(num_1), ConstrainedValue::Integer(num_2)) => {
Ok(ConstrainedValue::Boolean(Boolean::Constant(num_1.eq(&num_2))))
num_1.evaluate_equal(expression_namespace, &num_2)?
}
(ConstrainedValue::Field(fe_1), ConstrainedValue::Field(fe_2)) => {
Ok(ConstrainedValue::Boolean(Boolean::Constant(fe_1.eq(&fe_2))))
fe_1.evaluate_equal(expression_namespace, &fe_2)?
}
(ConstrainedValue::Group(ge_1), ConstrainedValue::Group(ge_2)) => {
Ok(ConstrainedValue::Boolean(Boolean::Constant(ge_1.eq(&ge_2))))
ge_1.evaluate_equal(expression_namespace, &ge_2)?
}
(ConstrainedValue::Unresolved(string), val_2) => {
let val_1 = ConstrainedValue::from_other(string, &val_2)?;
self.evaluate_eq_expression(val_1, val_2)
return self.evaluate_eq_expression(&mut expression_namespace, val_1, val_2);
}
(val_1, ConstrainedValue::Unresolved(string)) => {
let val_2 = ConstrainedValue::from_other(string, &val_1)?;
self.evaluate_eq_expression(val_1, val_2)
return self.evaluate_eq_expression(&mut expression_namespace, val_1, val_2);
}
(val_1, val_2) => Err(ExpressionError::IncompatibleTypes(format!("{} == {}", val_1, val_2,))),
}
(val_1, val_2) => return Err(ExpressionError::IncompatibleTypes(format!("{} == {}", val_1, val_2,))),
};
Ok(ConstrainedValue::Boolean(result_bool))
}
//TODO: unsafe for allocated values
fn evaluate_ge_expression(
&mut self,
left: ConstrainedValue<F, G>,
@ -244,6 +249,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
}
}
//TODO: unsafe for allocated values
fn evaluate_gt_expression(
&mut self,
left: ConstrainedValue<F, G>,
@ -273,6 +279,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
}
}
//TODO: unsafe for allocated values
fn evaluate_le_expression(
&mut self,
left: ConstrainedValue<F, G>,
@ -302,6 +309,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
}
}
//TODO: unsafe for allocated values
fn evaluate_lt_expression(
&mut self,
left: ConstrainedValue<F, G>,
@ -354,8 +362,8 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
};
let resolved_second =
self.enforce_branch(cs, file_scope.clone(), function_scope.clone(), expected_types, second)?;
let resolved_third = self.enforce_branch(cs, file_scope, function_scope, expected_types, third)?;
self.enforce_expression_value(cs, file_scope.clone(), function_scope.clone(), expected_types, second)?;
let resolved_third = self.enforce_expression_value(cs, file_scope, function_scope, expected_types, third)?;
match (resolved_second, resolved_third) {
(ConstrainedValue::Boolean(bool_2), ConstrainedValue::Boolean(bool_3)) => {
@ -374,7 +382,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
let result = G::conditionally_select(cs, &resolved_first, &ge_1, &ge_2)?;
Ok(ConstrainedValue::Group(result))
}
(_, _) => unimplemented!("conditional select gadget not implemented between given types"),
(_, _) => unimplemented!("statements.conditional select gadget not implemented between given types"),
}
}
@ -451,7 +459,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
index: Expression,
) -> Result<usize, ExpressionError> {
let expected_types = vec![Type::IntegerType(IntegerType::U32)];
match self.enforce_branch(cs, file_scope.clone(), function_scope.clone(), &expected_types, index)? {
match self.enforce_expression_value(cs, file_scope.clone(), function_scope.clone(), &expected_types, index)? {
ConstrainedValue::Integer(number) => Ok(number.to_usize()),
value => Err(ExpressionError::InvalidIndex(value.to_string())),
}
@ -466,7 +474,13 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
array: Box<Expression>,
index: RangeOrExpression,
) -> Result<ConstrainedValue<F, G>, ExpressionError> {
let array = match self.enforce_branch(cs, file_scope.clone(), function_scope.clone(), expected_types, *array)? {
let array = match self.enforce_expression_value(
cs,
file_scope.clone(),
function_scope.clone(),
expected_types,
*array,
)? {
ConstrainedValue::Array(array) => array,
value => return Err(ExpressionError::InvalidArrayAccess(value.to_string())),
};
@ -562,7 +576,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
circuit_identifier: Box<Expression>,
circuit_member: Identifier,
) -> Result<ConstrainedValue<F, G>, ExpressionError> {
let (circuit_name, members) = match self.enforce_branch(
let (circuit_name, members) = match self.enforce_expression_value(
cs,
file_scope.clone(),
function_scope.clone(),
@ -712,7 +726,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
/// Enforce a branch of a binary expression.
/// We don't care about mutability because we are not changing any variables.
/// We try to resolve unresolved types here if the type is given explicitly.
pub(crate) fn enforce_branch<CS: ConstraintSystem<F>>(
pub(crate) fn enforce_expression_value<CS: ConstraintSystem<F>>(
&mut self,
cs: &mut CS,
file_scope: String,
@ -737,10 +751,12 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
left: Expression,
right: Expression,
) -> Result<(ConstrainedValue<F, G>, ConstrainedValue<F, G>), ExpressionError> {
let resolved_left =
self.enforce_branch(cs, file_scope.clone(), function_scope.clone(), expected_types, left)?;
let resolved_right =
self.enforce_branch(cs, file_scope.clone(), function_scope.clone(), expected_types, right)?;
let mut resolved_left =
self.enforce_expression_value(cs, file_scope.clone(), function_scope.clone(), expected_types, left)?;
let mut resolved_right =
self.enforce_expression_value(cs, file_scope.clone(), function_scope.clone(), expected_types, right)?;
resolved_left.resolve_types(&mut resolved_right, expected_types)?;
Ok((resolved_left, resolved_right))
}
@ -763,7 +779,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
Expression::Integer(integer) => Ok(ConstrainedValue::Integer(integer)),
Expression::Field(field) => Ok(ConstrainedValue::Field(FieldType::constant(field)?)),
Expression::Group(group_affine) => Ok(ConstrainedValue::Group(G::constant(group_affine)?)),
Expression::Boolean(bool) => Ok(Self::get_boolean_constant(bool)),
Expression::Boolean(bool) => Ok(ConstrainedValue::Boolean(bool)),
Expression::Implicit(value) => Self::enforce_number_implicit(expected_types, value),
// Binary operations
@ -865,19 +881,19 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
cs,
file_scope.clone(),
function_scope.clone(),
expected_types,
&vec![],
*left,
*right,
)?;
Ok(self.evaluate_eq_expression(resolved_left, resolved_right)?)
Ok(self.evaluate_eq_expression(cs, resolved_left, resolved_right)?)
}
Expression::Ge(left, right) => {
let (resolved_left, resolved_right) = self.enforce_binary_expression(
cs,
file_scope.clone(),
function_scope.clone(),
expected_types,
&vec![],
*left,
*right,
)?;
@ -889,7 +905,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
cs,
file_scope.clone(),
function_scope.clone(),
expected_types,
&vec![],
*left,
*right,
)?;
@ -901,7 +917,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
cs,
file_scope.clone(),
function_scope.clone(),
expected_types,
&vec![],
*left,
*right,
)?;
@ -913,7 +929,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
cs,
file_scope.clone(),
function_scope.clone(),
expected_types,
&vec![],
*left,
*right,
)?;

View File

@ -87,6 +87,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
cs,
scope.clone(),
function_name.clone(),
None,
statement.clone(),
function.returns.clone(),
)? {

View File

@ -23,7 +23,7 @@ use snarkos_models::{
curves::{Field, PrimeField},
gadgets::{
r1cs::ConstraintSystem,
utilities::{boolean::Boolean, eq::EqGadget, uint::UInt32},
utilities::{boolean::Boolean, eq::ConditionalEqGadget, select::CondSelectGadget, uint::UInt32},
},
};
@ -52,10 +52,13 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
cs: &mut CS,
file_scope: String,
function_scope: String,
indicator: Option<Boolean>,
name: String,
range_or_expression: RangeOrExpression,
new_value: ConstrainedValue<F, G>,
mut new_value: ConstrainedValue<F, G>,
) -> Result<(), StatementError> {
let condition = indicator.unwrap_or(Boolean::Constant(true));
// Resolve index so we know if we are assigning to a single value or a range of values
match range_or_expression {
RangeOrExpression::Expression(index) => {
@ -64,7 +67,14 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
// Modify the single value of the array in place
match self.get_mutable_assignee(name)? {
ConstrainedValue::Array(old) => {
old[index] = new_value;
new_value.resolve_type(&vec![old[index].to_type()])?;
let selected_value =
ConstrainedValue::conditionally_select(cs, &condition, &new_value, &old[index]).map_err(
|_| StatementError::SelectFail(new_value.to_string(), old[index].to_string()),
)?;
old[index] = selected_value;
}
_ => return Err(StatementError::ArrayAssignIndex),
}
@ -79,26 +89,36 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
None => None,
};
// Modify the range of values of the array in place
match (self.get_mutable_assignee(name)?, new_value) {
(ConstrainedValue::Array(old), ConstrainedValue::Array(ref new)) => {
let to_index = to_index_option.unwrap_or(old.len());
old.splice(from_index..to_index, new.iter().cloned());
// Modify the range of values of the array
let old_array = self.get_mutable_assignee(name)?;
let new_array = match (old_array.clone(), new_value) {
(ConstrainedValue::Array(mut mutable), ConstrainedValue::Array(new)) => {
let to_index = to_index_option.unwrap_or(mutable.len());
mutable.splice(from_index..to_index, new.iter().cloned());
ConstrainedValue::Array(mutable)
}
_ => return Err(StatementError::ArrayAssignRange),
}
};
let selected_array = ConstrainedValue::conditionally_select(cs, &condition, &new_array, old_array)
.map_err(|_| StatementError::SelectFail(new_array.to_string(), old_array.to_string()))?;
*old_array = selected_array;
}
}
Ok(())
}
fn mutute_circuit_field(
fn mutute_circuit_field<CS: ConstraintSystem<F>>(
&mut self,
cs: &mut CS,
indicator: Option<Boolean>,
circuit_name: String,
object_name: Identifier,
new_value: ConstrainedValue<F, G>,
mut new_value: ConstrainedValue<F, G>,
) -> Result<(), StatementError> {
let condition = indicator.unwrap_or(Boolean::Constant(true));
match self.get_mutable_assignee(circuit_name)? {
ConstrainedValue::CircuitExpression(_variable, members) => {
// Modify the circuit field in place
@ -114,7 +134,16 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
ConstrainedValue::Static(_value) => {
return Err(StatementError::ImmutableCircuitFunction("static".into()));
}
_ => object.1 = new_value.to_owned(),
_ => {
new_value.resolve_type(&vec![object.1.to_type()])?;
let selected_value = ConstrainedValue::conditionally_select(
cs, &condition, &new_value, &object.1,
)
.map_err(|_| StatementError::SelectFail(new_value.to_string(), object.1.to_string()))?;
object.1 = selected_value.to_owned();
}
},
None => return Err(StatementError::UndefinedCircuitObject(object_name.to_string())),
}
@ -130,6 +159,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
cs: &mut CS,
file_scope: String,
function_scope: String,
indicator: Option<Boolean>,
assignee: Assignee,
expression: Expression,
) -> Result<(), StatementError> {
@ -137,14 +167,19 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
let variable_name = self.resolve_assignee(function_scope.clone(), assignee.clone());
// Evaluate new value
let new_value = self.enforce_expression(cs, file_scope.clone(), function_scope.clone(), &vec![], expression)?;
let mut new_value =
self.enforce_expression(cs, file_scope.clone(), function_scope.clone(), &vec![], expression)?;
// Mutate the old value into the new value
match assignee {
Assignee::Identifier(_identifier) => {
let condition = indicator.unwrap_or(Boolean::Constant(true));
let old_value = self.get_mutable_assignee(variable_name.clone())?;
new_value.resolve_type(&vec![old_value.to_type()])?;
let selected_value = ConstrainedValue::conditionally_select(cs, &condition, &new_value, old_value)
.map_err(|_| StatementError::SelectFail(new_value.to_string(), old_value.to_string()))?;
*old_value = new_value;
*old_value = selected_value;
Ok(())
}
@ -152,12 +187,13 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
cs,
file_scope,
function_scope,
indicator,
variable_name,
range_or_expression,
new_value,
),
Assignee::CircuitField(_assignee, object_name) => {
self.mutute_circuit_field(variable_name, object_name, new_value)
self.mutute_circuit_field(cs, indicator, variable_name, object_name, new_value)
}
}
}
@ -262,7 +298,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
let mut returns = vec![];
for (expression, ty) in expressions.into_iter().zip(return_types.into_iter()) {
let expected_types = vec![ty.clone()];
let result = self.enforce_branch(
let result = self.enforce_expression_value(
cs,
file_scope.clone(),
function_scope.clone(),
@ -276,11 +312,12 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
Ok(ConstrainedValue::Return(returns))
}
fn iterate_or_early_return<CS: ConstraintSystem<F>>(
fn evaluate_branch<CS: ConstraintSystem<F>>(
&mut self,
cs: &mut CS,
file_scope: String,
function_scope: String,
indicator: Option<Boolean>,
statements: Vec<Statement>,
return_types: Vec<Type>,
) -> Result<Option<ConstrainedValue<F, G>>, StatementError> {
@ -291,6 +328,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
cs,
file_scope.clone(),
function_scope.clone(),
indicator.clone(),
statement.clone(),
return_types.clone(),
)? {
@ -302,16 +340,24 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
Ok(res)
}
/// Enforces a statements.conditional statement with one or more branches.
/// Due to R1CS constraints, we must evaluate every branch to properly construct the circuit.
/// At program execution, we will pass an `indicator bit` down to all child statements within each branch.
/// The `indicator bit` will select that branch while keeping the constraint system satisfied.
fn enforce_conditional_statement<CS: ConstraintSystem<F>>(
&mut self,
cs: &mut CS,
file_scope: String,
function_scope: String,
indicator: Option<Boolean>,
statement: ConditionalStatement,
return_types: Vec<Type>,
) -> Result<Option<ConstrainedValue<F, G>>, StatementError> {
let statement_string = statement.to_string();
let outer_indicator = indicator.unwrap_or(Boolean::Constant(true));
let expected_types = vec![Type::Boolean];
let condition = match self.enforce_expression(
let inner_indicator = match self.enforce_expression(
cs,
file_scope.clone(),
function_scope.clone(),
@ -322,21 +368,50 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
value => return Err(StatementError::IfElseConditional(value.to_string())),
};
// use gadget impl
if condition.eq(&Boolean::Constant(true)) {
self.iterate_or_early_return(cs, file_scope, function_scope, statement.statements, return_types)
} else {
match statement.next {
Some(next) => match next {
ConditionalNestedOrEndStatement::Nested(nested) => {
self.enforce_conditional_statement(cs, file_scope, function_scope, *nested, return_types)
}
ConditionalNestedOrEndStatement::End(statements) => {
self.iterate_or_early_return(cs, file_scope, function_scope, statements, return_types)
}
},
None => Ok(None),
}
// Determine nested branch selection
let branch_1_indicator = Boolean::and(
&mut cs.ns(|| format!("statement branch 1 indicator {}", statement_string)),
&outer_indicator,
&inner_indicator,
)?;
// Execute branch 1
self.evaluate_branch(
cs,
file_scope.clone(),
function_scope.clone(),
Some(branch_1_indicator),
statement.statements,
return_types.clone(),
)?;
// Execute branch 2
let branch_2_indicator = Boolean::and(
&mut cs.ns(|| format!("statement branch 2 indicator {}", statement_string)),
&outer_indicator,
&inner_indicator.not(),
)?;
match statement.next {
Some(next) => match next {
ConditionalNestedOrEndStatement::Nested(nested) => self.enforce_conditional_statement(
cs,
file_scope,
function_scope,
Some(branch_2_indicator),
*nested,
return_types,
),
ConditionalNestedOrEndStatement::End(statements) => self.evaluate_branch(
cs,
file_scope,
function_scope,
Some(branch_2_indicator),
statements,
return_types,
),
},
None => Ok(None), // this is an if with no else, have to pass statements.conditional down to next statements somehow
}
}
@ -345,6 +420,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
cs: &mut CS,
file_scope: String,
function_scope: String,
indicator: Option<Boolean>,
index: Identifier,
start: Integer,
stop: Integer,
@ -362,11 +438,14 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
ConstrainedValue::Integer(Integer::U32(UInt32::constant(i as u32))),
);
cs.ns(|| format!("loop {} = {}", index.to_string(), i));
// Evaluate statements and possibly return early
if let Some(early_return) = self.iterate_or_early_return(
if let Some(early_return) = self.evaluate_branch(
cs,
file_scope.clone(),
function_scope.clone(),
indicator,
statements.clone(),
return_types.clone(),
)? {
@ -381,29 +460,14 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
fn enforce_assert_eq_statement<CS: ConstraintSystem<F>>(
&mut self,
cs: &mut CS,
left: ConstrainedValue<F, G>,
right: ConstrainedValue<F, G>,
indicator: Option<Boolean>,
left: &ConstrainedValue<F, G>,
right: &ConstrainedValue<F, G>,
) -> Result<(), StatementError> {
Ok(match (left, right) {
(ConstrainedValue::Boolean(bool_1), ConstrainedValue::Boolean(bool_2)) => {
self.enforce_boolean_eq(cs, bool_1, bool_2)?
}
(ConstrainedValue::Integer(num_1), ConstrainedValue::Integer(num_2)) => num_1
.enforce_equal(cs, &num_2)
.map_err(|_| StatementError::AssertionFailed(num_1.to_string(), num_2.to_string()))?,
(ConstrainedValue::Field(fe_1), ConstrainedValue::Field(fe_2)) => fe_1
.enforce_equal(cs, &fe_2)
.map_err(|_| StatementError::AssertionFailed(fe_1.to_string(), fe_2.to_string()))?,
(ConstrainedValue::Group(ge_1), ConstrainedValue::Group(ge_2)) => ge_1
.enforce_equal(cs, &ge_2)
.map_err(|_| StatementError::AssertionFailed(ge_1.to_string(), ge_2.to_string()))?,
(ConstrainedValue::Array(arr_1), ConstrainedValue::Array(arr_2)) => {
for (left, right) in arr_1.into_iter().zip(arr_2.into_iter()) {
self.enforce_assert_eq_statement(cs, left, right)?;
}
}
(val_1, val_2) => return Err(StatementError::AssertEq(val_1.to_string(), val_2.to_string())),
})
let condition = indicator.unwrap_or(Boolean::Constant(true));
let result = left.conditional_enforce_equal(cs, right, &condition);
Ok(result.map_err(|_| StatementError::AssertionFailed(left.to_string(), right.to_string()))?)
}
pub(crate) fn enforce_statement<CS: ConstraintSystem<F>>(
@ -411,6 +475,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
cs: &mut CS,
file_scope: String,
function_scope: String,
indicator: Option<Boolean>,
statement: Statement,
return_types: Vec<Type>,
) -> Result<Option<ConstrainedValue<F, G>>, StatementError> {
@ -423,15 +488,20 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
self.enforce_definition_statement(cs, file_scope, function_scope, variable, expression)?;
}
Statement::Assign(variable, expression) => {
self.enforce_assign_statement(cs, file_scope, function_scope, variable, expression)?;
self.enforce_assign_statement(cs, file_scope, function_scope, indicator, variable, expression)?;
}
Statement::MultipleAssign(variables, function) => {
self.enforce_multiple_definition_statement(cs, file_scope, function_scope, variables, function)?;
}
Statement::Conditional(statement) => {
if let Some(early_return) =
self.enforce_conditional_statement(cs, file_scope, function_scope, statement, return_types)?
{
if let Some(early_return) = self.enforce_conditional_statement(
cs,
file_scope,
function_scope,
indicator,
statement,
return_types,
)? {
res = Some(early_return)
}
}
@ -440,6 +510,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
cs,
file_scope,
function_scope,
indicator,
index,
start,
stop,
@ -450,12 +521,10 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
}
}
Statement::AssertEq(left, right) => {
let resolved_left =
self.enforce_expression(cs, file_scope.clone(), function_scope.clone(), &vec![], left)?;
let resolved_right =
self.enforce_expression(cs, file_scope.clone(), function_scope.clone(), &vec![], right)?;
let (resolved_left, resolved_right) =
self.enforce_binary_expression(cs, file_scope, function_scope, &vec![], left, right)?;
self.enforce_assert_eq_statement(cs, resolved_left, resolved_right)?;
self.enforce_assert_eq_statement(cs, indicator, &resolved_left, &resolved_right)?;
}
Statement::Expression(expression) => {
match self.enforce_expression(cs, file_scope, function_scope, &vec![], expression.clone())? {

View File

@ -3,11 +3,17 @@
use crate::{errors::ValueError, FieldType, GroupType};
use leo_types::{Circuit, Function, Identifier, Integer, IntegerType, Type};
use snarkos_errors::gadgets::SynthesisError;
use snarkos_models::{
curves::{Field, PrimeField},
gadgets::utilities::{
boolean::Boolean,
uint::{UInt128, UInt16, UInt32, UInt64, UInt8},
gadgets::{
r1cs::ConstraintSystem,
utilities::{
boolean::Boolean,
eq::ConditionalEqGadget,
select::CondSelectGadget,
uint::{UInt128, UInt16, UInt32, UInt64, UInt8},
},
},
};
use std::fmt;
@ -79,6 +85,21 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedValue<F, G> {
Ok(())
}
/// Expect both `self` and `other` to resolve to the same type
pub(crate) fn resolve_types(&mut self, other: &mut Self, types: &Vec<Type>) -> Result<(), ValueError> {
if !types.is_empty() {
self.resolve_type(types)?;
return other.resolve_type(types);
}
match (&self, &other) {
(ConstrainedValue::Unresolved(_), ConstrainedValue::Unresolved(_)) => Ok(()),
(ConstrainedValue::Unresolved(_), _) => self.resolve_type(&vec![other.to_type()]),
(_, ConstrainedValue::Unresolved(_)) => other.resolve_type(&vec![self.to_type()]),
_ => Ok(()),
}
}
pub(crate) fn get_inner_mut(&mut self) {
if let ConstrainedValue::Mutable(inner) = self {
*self = *inner.clone()
@ -139,3 +160,118 @@ impl<F: Field + PrimeField, G: GroupType<F>> fmt::Debug for ConstrainedValue<F,
write!(f, "{}", self)
}
}
impl<F: Field + PrimeField, G: GroupType<F>> ConditionalEqGadget<F> for ConstrainedValue<F, G> {
fn conditional_enforce_equal<CS: ConstraintSystem<F>>(
&self,
mut cs: CS,
other: &Self,
condition: &Boolean,
) -> Result<(), SynthesisError> {
match (self, other) {
(ConstrainedValue::Boolean(bool_1), ConstrainedValue::Boolean(bool_2)) => bool_1.conditional_enforce_equal(
cs.ns(|| format!("{} == {}", self.to_string(), other.to_string())),
bool_2,
&condition,
),
(ConstrainedValue::Integer(num_1), ConstrainedValue::Integer(num_2)) => num_1.conditional_enforce_equal(
cs.ns(|| format!("{} == {}", self.to_string(), other.to_string())),
num_2,
&condition,
),
(ConstrainedValue::Field(field_1), ConstrainedValue::Field(field_2)) => field_1.conditional_enforce_equal(
cs.ns(|| format!("{} == {}", self.to_string(), other.to_string())),
field_2,
&condition,
),
(ConstrainedValue::Group(group_1), ConstrainedValue::Group(group_2)) => group_1.conditional_enforce_equal(
cs.ns(|| format!("{} == {}", self.to_string(), other.to_string())),
group_2,
&condition,
),
(ConstrainedValue::Array(arr_1), ConstrainedValue::Array(arr_2)) => {
for (i, (left, right)) in arr_1.into_iter().zip(arr_2.into_iter()).enumerate() {
left.conditional_enforce_equal(
cs.ns(|| format!("array[{}] equal {} == {}", i, left.to_string(), right.to_string())),
right,
&condition,
)?;
}
Ok(())
}
(_, _) => return Err(SynthesisError::Unsatisfiable),
}
}
fn cost() -> usize {
unimplemented!()
}
}
impl<F: Field + PrimeField, G: GroupType<F>> CondSelectGadget<F> for ConstrainedValue<F, G> {
fn conditionally_select<CS: ConstraintSystem<F>>(
mut cs: CS,
cond: &Boolean,
first: &Self,
second: &Self,
) -> Result<Self, SynthesisError> {
Ok(match (first, second) {
(ConstrainedValue::Boolean(bool_1), ConstrainedValue::Boolean(bool_2)) => {
ConstrainedValue::Boolean(Boolean::conditionally_select(
cs.ns(|| format!("if cond ? {} else {}", first.to_string(), second.to_string())),
cond,
bool_1,
bool_2,
)?)
}
(ConstrainedValue::Integer(num_1), ConstrainedValue::Integer(num_2)) => {
ConstrainedValue::Integer(Integer::conditionally_select(
cs.ns(|| format!("if cond ? {} else {}", first.to_string(), second.to_string())),
cond,
num_1,
num_2,
)?)
}
(ConstrainedValue::Field(field_1), ConstrainedValue::Field(field_2)) => {
ConstrainedValue::Field(FieldType::conditionally_select(
cs.ns(|| format!("if cond ? {} else {}", first.to_string(), second.to_string())),
cond,
field_1,
field_2,
)?)
}
(ConstrainedValue::Group(group_1), ConstrainedValue::Group(group_2)) => {
ConstrainedValue::Group(G::conditionally_select(
cs.ns(|| format!("if cond ? {} else {}", first.to_string(), second.to_string())),
cond,
group_1,
group_2,
)?)
}
(ConstrainedValue::Array(arr_1), ConstrainedValue::Array(arr_2)) => {
let mut array = vec![];
for (i, (first, second)) in arr_1.into_iter().zip(arr_2.into_iter()).enumerate() {
array.push(Self::conditionally_select(
cs.ns(|| {
format!(
"array[{}] = if cond ? {} else {}",
i,
first.to_string(),
second.to_string()
)
}),
cond,
first,
second,
)?);
}
ConstrainedValue::Array(array)
}
(_, _) => return Err(SynthesisError::Unsatisfiable),
})
}
fn cost() -> usize {
unimplemented!() //lower bound 1, upper bound 128 or length of static array
}
}

View File

@ -81,7 +81,7 @@ pub enum ExpressionError {
UndefinedFunction(String),
// Conditionals
#[error("If, else conditional must resolve to a boolean, got {}", _0)]
#[error("If, else statements.conditional must resolve to a boolean, got {}", _0)]
IfElseConditional(String),
#[error("{}", _0)]

View File

@ -1,4 +1,4 @@
use crate::errors::{BooleanError, ExpressionError};
use crate::errors::{BooleanError, ExpressionError, ValueError};
use snarkos_errors::gadgets::SynthesisError;
@ -37,7 +37,7 @@ pub enum StatementError {
#[error("Assertion {:?} == {:?} failed", _0, _1)]
AssertionFailed(String, String),
#[error("If, else conditional must resolve to a boolean, got {}", _0)]
#[error("If, else statements.conditional must resolve to a boolean, got {}", _0)]
IfElseConditional(String),
#[error("Cannot assign to immutable variable {}", _0)]
@ -49,9 +49,15 @@ pub enum StatementError {
#[error("Function return statement expected {} return values, got {}", _0, _1)]
InvalidNumberOfReturns(usize, usize),
#[error("Conditional select gadget failed to select between {} or {}", _0, _1)]
SelectFail(String, String),
#[error("{}", _0)]
SynthesisError(#[from] SynthesisError),
#[error("Expected assignment of return values for expression {}", _0)]
Unassigned(String),
#[error("{}", _0)]
ValueError(#[from] ValueError),
}

View File

@ -11,7 +11,7 @@ use snarkos_models::{
utilities::{
alloc::AllocGadget,
boolean::Boolean,
eq::{ConditionalEqGadget, EqGadget},
eq::{ConditionalEqGadget, EqGadget, EvaluateEqGadget},
select::CondSelectGadget,
uint::UInt8,
ToBitsGadget,
@ -175,6 +175,22 @@ impl<F: Field + PrimeField> PartialOrd for FieldType<F> {
}
}
impl<F: Field + PrimeField> EvaluateEqGadget<F> for FieldType<F> {
fn evaluate_equal<CS: ConstraintSystem<F>>(&self, mut cs: CS, other: &Self) -> Result<Boolean, SynthesisError> {
match (self, other) {
(FieldType::Constant(first), FieldType::Constant(second)) => Ok(Boolean::constant(first.eq(second))),
(FieldType::Allocated(allocated), FieldType::Constant(constant))
| (FieldType::Constant(constant), FieldType::Allocated(allocated)) => {
let bool_option = allocated.value.map(|f| f.eq(constant));
Boolean::alloc(&mut cs.ns(|| "evaluate_equal"), || {
bool_option.ok_or(SynthesisError::AssignmentMissing)
})
}
(FieldType::Allocated(first), FieldType::Allocated(second)) => first.evaluate_equal(cs, second),
}
}
}
impl<F: Field + PrimeField> EqGadget<F> for FieldType<F> {}
impl<F: Field + PrimeField> ConditionalEqGadget<F> for FieldType<F> {

View File

@ -14,7 +14,7 @@ use snarkos_models::{
utilities::{
alloc::AllocGadget,
boolean::Boolean,
eq::{ConditionalEqGadget, EqGadget},
eq::{ConditionalEqGadget, EqGadget, EvaluateEqGadget},
select::CondSelectGadget,
uint::UInt8,
ToBitsGadget,
@ -165,6 +165,40 @@ impl PartialEq for EdwardsGroupType {
impl Eq for EdwardsGroupType {}
impl EvaluateEqGadget<Fq> for EdwardsGroupType {
fn evaluate_equal<CS: ConstraintSystem<Fq>>(&self, mut cs: CS, other: &Self) -> Result<Boolean, SynthesisError> {
match (self, other) {
(EdwardsGroupType::Constant(self_value), EdwardsGroupType::Constant(other_value)) => {
Ok(Boolean::Constant(self_value == other_value))
}
(EdwardsGroupType::Allocated(self_value), EdwardsGroupType::Allocated(other_value)) => {
let bool_option =
<EdwardsBlsGadget as GroupGadget<GroupAffine<EdwardsParameters>, Fq>>::get_value(self_value)
.and_then(|a| {
<EdwardsBlsGadget as GroupGadget<GroupAffine<EdwardsParameters>, Fq>>::get_value(
other_value,
)
.map(|b| a.eq(&b))
});
Boolean::alloc(&mut cs.ns(|| "evaluate_equal"), || {
bool_option.ok_or(SynthesisError::AssignmentMissing)
})
}
(EdwardsGroupType::Constant(constant_value), EdwardsGroupType::Allocated(allocated_value))
| (EdwardsGroupType::Allocated(allocated_value), EdwardsGroupType::Constant(constant_value)) => {
let bool_option =
<EdwardsBlsGadget as GroupGadget<GroupAffine<EdwardsParameters>, Fq>>::get_value(allocated_value)
.map(|a| a.eq(constant_value));
Boolean::alloc(&mut cs.ns(|| "evaluate_equal"), || {
bool_option.ok_or(SynthesisError::AssignmentMissing)
})
}
}
}
}
impl EqGadget<Fq> for EdwardsGroupType {}
impl ConditionalEqGadget<Fq> for EdwardsGroupType {

View File

@ -8,7 +8,7 @@ use snarkos_models::{
r1cs::ConstraintSystem,
utilities::{
alloc::AllocGadget,
eq::{ConditionalEqGadget, EqGadget},
eq::{ConditionalEqGadget, EqGadget, EvaluateEqGadget},
select::CondSelectGadget,
ToBitsGadget,
ToBytesGadget,
@ -24,6 +24,7 @@ pub trait GroupType<F: Field>:
+ Clone
+ Debug
+ Display
+ EvaluateEqGadget<F>
+ EqGadget<F>
+ ConditionalEqGadget<F>
+ AllocGadget<String, F>

View File

@ -118,11 +118,6 @@ macro_rules! test_uint {
let r1: $_type = rand::random();
let r2: $_type = rand::random();
let quotient = r1.wrapping_div(r2);
let cs = TestConstraintSystem::<Fq>::new();
let quotient_allocated = <$gadget>::alloc(cs, || Ok(quotient)).unwrap();
let bytes = include_bytes!("div.leo");
let mut program = parse_program(bytes).unwrap();
@ -131,7 +126,16 @@ macro_rules! test_uint {
Some(InputValue::Integer($integer_type, r2 as u128)),
]);
output_expected_allocated(program, quotient_allocated);
// expect an error when dividing by zero
if r2 == 0 {
let _err = get_error(program);
} else {
let cs = TestConstraintSystem::<Fq>::new();
let quotient = r1.wrapping_div(r2);
let quotient_allocated = <$gadget>::alloc(cs, || Ok(quotient)).unwrap();
output_expected_allocated(program, quotient_allocated);
}
}
}

View File

@ -1,5 +1,6 @@
use crate::{
boolean::{output_expected_boolean, output_false, output_true},
get_error,
get_output,
integers::{fail_integer, fail_synthesis, IntegerTester},
parse_program,

View File

@ -1,5 +1,6 @@
use crate::{
boolean::{output_expected_boolean, output_false, output_true},
get_error,
get_output,
integers::{fail_integer, fail_synthesis, IntegerTester},
parse_program,

View File

@ -1,5 +1,6 @@
use crate::{
boolean::{output_expected_boolean, output_false, output_true},
get_error,
get_output,
integers::{fail_integer, fail_synthesis, IntegerTester},
parse_program,
@ -28,22 +29,21 @@ fn output_expected_allocated(program: EdwardsTestCompiler, expected: UInt32) {
}
}
pub(crate) fn output_zero(program: EdwardsTestCompiler) {
pub(crate) fn output_number(program: EdwardsTestCompiler, number: u32) {
let output = get_output(program);
assert_eq!(
EdwardsConstrainedValue::Return(vec![ConstrainedValue::Integer(Integer::U32(UInt32::constant(0u32)))])
EdwardsConstrainedValue::Return(vec![ConstrainedValue::Integer(Integer::U32(UInt32::constant(number)))])
.to_string(),
output.to_string()
)
}
pub(crate) fn output_zero(program: EdwardsTestCompiler) {
output_number(program, 0u32);
}
pub(crate) fn output_one(program: EdwardsTestCompiler) {
let output = get_output(program);
assert_eq!(
EdwardsConstrainedValue::Return(vec![ConstrainedValue::Integer(Integer::U32(UInt32::constant(1u32)))])
.to_string(),
output.to_string()
)
output_number(program, 1u32);
}
#[test]

View File

@ -1,5 +1,6 @@
use crate::{
boolean::{output_expected_boolean, output_false, output_true},
get_error,
get_output,
integers::{fail_integer, fail_synthesis, IntegerTester},
parse_program,

View File

@ -1,5 +1,6 @@
use crate::{
boolean::{output_expected_boolean, output_false, output_true},
get_error,
get_output,
integers::{fail_integer, fail_synthesis, IntegerTester},
parse_program,

View File

@ -0,0 +1,7 @@
function main(bit: private u32) {
if bit == 1 {
assert_eq!(bit, 1);
} else {
assert_eq!(bit, 0);
}
}

View File

@ -0,0 +1,13 @@
function main(bit: u32) -> u32 {
let mut result = 0u32;
if bit == 1 {
result = 1;
} else if bit == 2 {
result = 2;
} else {
result = 3;
}
return result
}

View File

@ -0,0 +1,11 @@
function main(cond: bool) -> u32 {
let mut a = 0u32;
if cond {
for i in 0..4 {
a += i;
}
}
return a
}

View File

@ -0,0 +1,130 @@
use crate::{
get_output,
integers::u32::{output_one, output_zero},
parse_program,
EdwardsConstrainedValue,
EdwardsTestCompiler,
};
use leo_inputs::types::{IntegerType, U32Type};
use leo_types::InputValue;
use crate::integers::u32::output_number;
use snarkos_curves::edwards_bls12::Fq;
use snarkos_models::gadgets::r1cs::TestConstraintSystem;
fn empty_output_satisfied(program: EdwardsTestCompiler) {
let output = get_output(program);
assert_eq!(EdwardsConstrainedValue::Return(vec![]).to_string(), output.to_string());
}
// Tests a statements.conditional enforceBit() program
//
// function main(bit: private u8) {
// if bit == 1u8 {
// assert_eq!(bit, 1u8);
// } else {
// assert_eq!(bit, 0u8);
// }
// }
#[test]
fn test_assert() {
let bytes = include_bytes!("assert.leo");
let mut program_1_pass = parse_program(bytes).unwrap();
let mut program_0_pass = program_1_pass.clone();
let mut program_2_fail = program_1_pass.clone();
// Check that an input value of 1 satisfies the constraint system
program_1_pass.set_inputs(vec![Some(InputValue::Integer(IntegerType::U32Type(U32Type {}), 1))]);
empty_output_satisfied(program_1_pass);
// Check that an input value of 0 satisfies the constraint system
program_0_pass.set_inputs(vec![Some(InputValue::Integer(IntegerType::U32Type(U32Type {}), 0))]);
empty_output_satisfied(program_0_pass);
// Check that an input value of 2 does not satisfy the constraint system
program_2_fail.set_inputs(vec![Some(InputValue::Integer(IntegerType::U32Type(U32Type {}), 2))]);
let mut cs = TestConstraintSystem::<Fq>::new();
let _output = program_2_fail.compile_constraints(&mut cs).unwrap();
assert!(!cs.is_satisfied());
}
#[test]
fn test_mutate() {
let bytes = include_bytes!("mutate.leo");
let mut program_1_true = parse_program(bytes).unwrap();
let mut program_0_pass = program_1_true.clone();
// Check that an input value of 1 satisfies the constraint system
program_1_true.set_inputs(vec![Some(InputValue::Integer(IntegerType::U32Type(U32Type {}), 1))]);
output_one(program_1_true);
// Check that an input value of 0 satisfies the constraint system
program_0_pass.set_inputs(vec![Some(InputValue::Integer(IntegerType::U32Type(U32Type {}), 0))]);
output_zero(program_0_pass);
}
#[test]
fn test_for_loop() {
let bytes = include_bytes!("for_loop.leo");
let mut program_true_6 = parse_program(bytes).unwrap();
let mut program_false_0 = program_true_6.clone();
// Check that an input value of true satisfies the constraint system
program_true_6.set_inputs(vec![Some(InputValue::Boolean(true))]);
output_number(program_true_6, 6u32);
// Check that an input value of false satisfies the constraint system
program_false_0.set_inputs(vec![Some(InputValue::Boolean(false))]);
output_zero(program_false_0);
}
#[test]
fn test_chain() {
let bytes = include_bytes!("chain.leo");
let mut program_1_1 = parse_program(bytes).unwrap();
let mut program_2_2 = program_1_1.clone();
let mut program_2_3 = program_1_1.clone();
// Check that an input of 1 outputs true
program_1_1.set_inputs(vec![Some(InputValue::Integer(IntegerType::U32Type(U32Type {}), 1))]);
output_number(program_1_1, 1u32);
// Check that an input of 0 outputs true
program_2_2.set_inputs(vec![Some(InputValue::Integer(IntegerType::U32Type(U32Type {}), 2))]);
output_number(program_2_2, 2u32);
// Check that an input of 0 outputs true
program_2_3.set_inputs(vec![Some(InputValue::Integer(IntegerType::U32Type(U32Type {}), 5))]);
output_number(program_2_3, 3u32);
}
#[test]
fn test_nested() {
let bytes = include_bytes!("nested.leo");
let mut program_true_true_3 = parse_program(bytes).unwrap();
let mut program_true_false_1 = program_true_true_3.clone();
let mut program_false_false_0 = program_true_true_3.clone();
// Check that an input value of true true satisfies the constraint system
program_true_true_3.set_inputs(vec![Some(InputValue::Boolean(true)); 2]);
output_number(program_true_true_3, 3u32);
// Check that an input value of true false satisfies the constraint system
program_true_false_1.set_inputs(vec![Some(InputValue::Boolean(true)), Some(InputValue::Boolean(false))]);
output_number(program_true_false_1, 1u32);
// Check that an input value of false false satisfies the constraint system
program_false_false_0.set_inputs(vec![Some(InputValue::Boolean(false)), Some(InputValue::Boolean(false))]);
output_number(program_false_false_0, 0u32);
}

View File

@ -0,0 +1,11 @@
function main(bit: private u32) -> u32 {
let mut a = 5u32;
if bit == 1 {
a = 1;
} else {
a = 0;
}
return a
}

View File

@ -0,0 +1,12 @@
function main(a: bool, b: bool) -> u32 {
let mut result = 0u32;
if a {
result += 1;
if b {
result += 2;
}
}
return result
}

View File

@ -7,6 +7,8 @@ use leo_types::InputValue;
use snarkos_curves::edwards_bls12::Fq;
use snarkos_models::gadgets::r1cs::TestConstraintSystem;
pub mod conditional;
// Ternary if {bool}? {expression} : {expression};
#[test]

View File

@ -12,3 +12,16 @@ function fibonacci(i: u32) -> u32 {
function main() -> u32 {
return fibonacci(1)
}
Function mutateNoLet(b: bool) {
let mut a = 5;
if b {
// must be turned into statements.conditional expression
a = 0;
// a = if b ? 0 : a;
} else {
a = 3;
// a = if b ? a : 3;
}
}

View File

@ -31,7 +31,7 @@ impl Inputs {
}
pub fn from_inputs_file(file: File, expected_inputs: Vec<FunctionInput>) -> Result<Self, InputParserError> {
let mut private = vec![];
let mut program_inputs = vec![];
let mut public = vec![];
for section in file.sections.into_iter() {
@ -62,7 +62,7 @@ impl Inputs {
}
// push value to vector
private.push(Some(value));
program_inputs.push(Some(value));
}
None => return Err(InputParserError::InputNotFound(input.to_string())),
}
@ -70,10 +70,7 @@ impl Inputs {
}
}
Ok(Self {
program_inputs: private,
public,
})
Ok(Self { program_inputs, public })
}
pub fn get_public_inputs<E: PairingEngine>(&self) -> Result<Vec<E::Fr>, InputParserError> {

View File

@ -18,6 +18,7 @@ use snarkos_models::{
},
};
use snarkos_models::gadgets::utilities::eq::EvaluateEqGadget;
use std::fmt;
/// An integer type enum wrapping the integer value. Used only in expressions.
@ -374,6 +375,19 @@ impl Integer {
}
}
impl<F: Field + PrimeField> EvaluateEqGadget<F> for Integer {
fn evaluate_equal<CS: ConstraintSystem<F>>(&self, cs: CS, other: &Self) -> Result<Boolean, SynthesisError> {
match (self, other) {
(Integer::U8(left_u8), Integer::U8(right_u8)) => left_u8.evaluate_equal(cs, right_u8),
(Integer::U16(left_u16), Integer::U16(right_u16)) => left_u16.evaluate_equal(cs, right_u16),
(Integer::U32(left_u32), Integer::U32(right_u32)) => left_u32.evaluate_equal(cs, right_u32),
(Integer::U64(left_u64), Integer::U64(right_u64)) => left_u64.evaluate_equal(cs, right_u64),
(Integer::U128(left_u128), Integer::U128(right_u128)) => left_u128.evaluate_equal(cs, right_u128),
(_, _) => Err(SynthesisError::AssignmentMissing),
}
}
}
impl<F: Field + PrimeField> EqGadget<F> for Integer {}
impl<F: Field + PrimeField> ConditionalEqGadget<F> for Integer {
@ -440,6 +454,16 @@ impl<F: Field + PrimeField> CondSelectGadget<F> for Integer {
impl fmt::Display for Integer {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}{}", self.to_usize(), self.get_type())
let option = match self {
Integer::U8(u8) => u8.value.map(|num| num as usize),
Integer::U16(u16) => u16.value.map(|num| num as usize),
Integer::U32(u32) => u32.value.map(|num| num as usize),
Integer::U64(u64) => u64.value.map(|num| num as usize),
Integer::U128(u128) => u128.value.map(|num| num as usize),
};
match option {
Some(number) => write!(f, "{}{}", number, self.get_type()),
None => write!(f, "[input]{}", self.get_type()),
}
}
}