Merge pull request #11 from AleoHQ/feature/implicit-types

Feature/implicit types
This commit is contained in:
Collin Chin 2020-05-20 16:23:09 -07:00 committed by GitHub
commit 9a1572b14b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 709 additions and 313 deletions

1
Cargo.lock generated
View File

@ -528,6 +528,7 @@ dependencies = [
"snarkos-errors",
"snarkos-gadgets",
"snarkos-models",
"snarkos-utilities",
"thiserror",
]

View File

@ -1,29 +1,10 @@
circuit PedersenHash {
parameters: group[1]
static function new(parameters: group[1]) -> Self {
return Self { parameters: parameters }
}
function hash(bits: bool[1]) -> group {
let mut digest: group = 0group;
for i in 0..1 {
let base: group = if bits[i] ? parameters[i] : 0group;
digest += base;
}
return digest
}
function test(mut a: u32) {
a = 0;
}
function main() -> group {
let parameters = [0group; 1];
let pedersen = PedersenHash::new(parameters);
function main() -> u32 {
let a = 1;
test(a);
let input: bool[1] = [true];
let output = pedersen.hash(input);
return output
return a // <- returns 1
}

View File

@ -10,6 +10,7 @@ snarkos-curves = { path = "../../snarkOS/curves", version = "0.8.0" }
snarkos-errors = { path = "../../snarkOS/errors", version = "0.8.0" }
snarkos-gadgets = { path = "../../snarkOS/gadgets", version = "0.8.0" }
snarkos-models = { path = "../../snarkOS/models", version = "0.8.0" }
snarkos-utilities = { path = "../../snarkOS/utilities", version = "0.8.0" }
thiserror = { version = "1.0" }
from-pest = { version = "0.3.1" }

View File

@ -247,11 +247,25 @@ impl<'ast> fmt::Display for Number<'ast> {
}
}
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::value_implicit))]
pub struct NumberImplicit<'ast> {
pub number: Number<'ast>,
#[pest_ast(outer())]
pub span: Span<'ast>,
}
impl<'ast> fmt::Display for NumberImplicit<'ast> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.number)
}
}
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::value_integer))]
pub struct Integer<'ast> {
pub number: Number<'ast>,
pub _type: Option<IntegerType>,
pub _type: IntegerType,
#[pest_ast(outer())]
pub span: Span<'ast>,
}
@ -314,6 +328,7 @@ pub enum Value<'ast> {
Field(Field<'ast>),
Group(Group<'ast>),
Boolean(Boolean<'ast>),
Implicit(NumberImplicit<'ast>),
}
impl<'ast> Value<'ast> {
@ -323,6 +338,7 @@ impl<'ast> Value<'ast> {
Value::Field(value) => &value.span,
Value::Group(value) => &value.span,
Value::Boolean(value) => &value.span,
Value::Implicit(value) => &value.span,
}
}
}
@ -334,6 +350,7 @@ impl<'ast> fmt::Display for Value<'ast> {
Value::Field(ref value) => write!(f, "{}", value),
Value::Group(ref value) => write!(f, "{}", value),
Value::Boolean(ref value) => write!(f, "{}", value),
Value::Implicit(ref value) => write!(f, "{}", value),
}
}
}

View File

@ -10,11 +10,15 @@ use crate::{
CircuitFieldDefinition, CircuitMember, Expression, Identifier, RangeOrExpression,
SpreadOrExpression,
},
Integer, Type,
};
use snarkos_models::{
curves::{Field, Group, PrimeField},
gadgets::{r1cs::ConstraintSystem, utilities::boolean::Boolean},
gadgets::{
r1cs::ConstraintSystem,
utilities::{boolean::Boolean, select::CondSelectGadget},
},
};
impl<F: Field + PrimeField, G: Group, CS: ConstraintSystem<F>> ConstrainedProgram<F, G, CS> {
@ -23,22 +27,30 @@ impl<F: Field + PrimeField, G: Group, CS: ConstraintSystem<F>> ConstrainedProgra
&mut self,
file_scope: String,
function_scope: String,
expected_types: Vec<Type<F, G>>,
unresolved_identifier: Identifier<F, G>,
) -> Result<ConstrainedValue<F, G>, ExpressionError> {
// Evaluate the identifier name in the current function scope
let variable_name = new_scope(function_scope, unresolved_identifier.to_string());
let identifier_name = new_scope(file_scope, unresolved_identifier.to_string());
if let Some(variable) = self.get(&variable_name) {
let result_value = if let Some(value) = self.get(&variable_name) {
// Reassigning variable to another variable
Ok(variable.clone())
} else if let Some(identifier) = self.get(&identifier_name) {
value.clone()
} else if let Some(value) = self.get(&identifier_name) {
// Check global scope (function and circuit names)
Ok(identifier.clone())
value.clone()
} else {
Err(ExpressionError::UndefinedIdentifier(
return Err(ExpressionError::UndefinedIdentifier(
unresolved_identifier.to_string(),
))
));
};
match result_value {
ConstrainedValue::Unresolved(string) => {
Self::enforce_number_implicit(expected_types, string)
}
value => Ok(value),
}
}
@ -49,30 +61,35 @@ impl<F: Field + PrimeField, G: Group, CS: ConstraintSystem<F>> ConstrainedProgra
left: ConstrainedValue<F, G>,
right: ConstrainedValue<F, G>,
) -> Result<ConstrainedValue<F, G>, ExpressionError> {
Ok(match (left, right) {
match (left, right) {
(ConstrainedValue::Integer(num_1), ConstrainedValue::Integer(num_2)) => {
Self::enforce_integer_add(cs, num_1, num_2)?
Ok(Self::enforce_integer_add(cs, num_1, num_2)?)
}
(ConstrainedValue::FieldElement(fe_1), ConstrainedValue::FieldElement(fe_2)) => {
self.enforce_field_add(cs, fe_1, fe_2)?
Ok(self.enforce_field_add(cs, fe_1, fe_2)?)
}
(ConstrainedValue::GroupElement(ge_1), ConstrainedValue::GroupElement(ge_2)) => {
Self::evaluate_group_add(ge_1, ge_2)
Ok(Self::evaluate_group_add(ge_1, ge_2))
}
(ConstrainedValue::Mutable(val_1), val_2) => {
self.enforce_add_expression(cs, *val_1, val_2)?
self.enforce_add_expression(cs, *val_1, val_2)
}
(val_1, ConstrainedValue::Mutable(val_2)) => {
self.enforce_add_expression(cs, val_1, *val_2)?
self.enforce_add_expression(cs, val_1, *val_2)
}
(val_1, val_2) => {
println!("not both groups");
return Err(ExpressionError::IncompatibleTypes(format!(
"{} + {}",
val_1, val_2,
)));
(ConstrainedValue::Unresolved(string), val_2) => {
let val_1 = ConstrainedValue::from_other(string, &val_2)?;
self.enforce_add_expression(cs, val_1, val_2)
}
})
(val_1, ConstrainedValue::Unresolved(string)) => {
let val_2 = ConstrainedValue::from_other(string, &val_1)?;
self.enforce_add_expression(cs, val_1, val_2)
}
(val_1, val_2) => Err(ExpressionError::IncompatibleTypes(format!(
"{} + {}",
val_1, val_2,
))),
}
}
fn enforce_sub_expression(
@ -81,29 +98,35 @@ impl<F: Field + PrimeField, G: Group, CS: ConstraintSystem<F>> ConstrainedProgra
left: ConstrainedValue<F, G>,
right: ConstrainedValue<F, G>,
) -> Result<ConstrainedValue<F, G>, ExpressionError> {
Ok(match (left, right) {
match (left, right) {
(ConstrainedValue::Integer(num_1), ConstrainedValue::Integer(num_2)) => {
Self::enforce_integer_sub(cs, num_1, num_2)?
Ok(Self::enforce_integer_sub(cs, num_1, num_2)?)
}
(ConstrainedValue::FieldElement(fe_1), ConstrainedValue::FieldElement(fe_2)) => {
self.enforce_field_sub(cs, fe_1, fe_2)?
Ok(self.enforce_field_sub(cs, fe_1, fe_2)?)
}
(ConstrainedValue::GroupElement(ge_1), ConstrainedValue::GroupElement(ge_2)) => {
Self::evaluate_group_sub(ge_1, ge_2)
Ok(Self::evaluate_group_sub(ge_1, ge_2))
}
(ConstrainedValue::Mutable(val_1), val_2) => {
self.enforce_sub_expression(cs, *val_1, val_2)?
self.enforce_sub_expression(cs, *val_1, val_2)
}
(val_1, ConstrainedValue::Mutable(val_2)) => {
self.enforce_sub_expression(cs, val_1, *val_2)?
self.enforce_sub_expression(cs, val_1, *val_2)
}
(val_1, val_2) => {
return Err(ExpressionError::IncompatibleTypes(format!(
"{} - {}",
val_1, val_2,
)))
(ConstrainedValue::Unresolved(string), val_2) => {
let val_1 = ConstrainedValue::from_other(string, &val_2)?;
self.enforce_sub_expression(cs, val_1, val_2)
}
})
(val_1, ConstrainedValue::Unresolved(string)) => {
let val_2 = ConstrainedValue::from_other(string, &val_1)?;
self.enforce_sub_expression(cs, val_1, val_2)
}
(val_1, val_2) => Err(ExpressionError::IncompatibleTypes(format!(
"{} - {}",
val_1, val_2,
))),
}
}
fn enforce_mul_expression(
@ -112,18 +135,29 @@ impl<F: Field + PrimeField, G: Group, CS: ConstraintSystem<F>> ConstrainedProgra
left: ConstrainedValue<F, G>,
right: ConstrainedValue<F, G>,
) -> Result<ConstrainedValue<F, G>, ExpressionError> {
Ok(match (left, right) {
match (left, right) {
(ConstrainedValue::Integer(num_1), ConstrainedValue::Integer(num_2)) => {
Self::enforce_integer_mul(cs, num_1, num_2)?
Ok(Self::enforce_integer_mul(cs, num_1, num_2)?)
}
(ConstrainedValue::FieldElement(fe_1), ConstrainedValue::FieldElement(fe_2)) => {
self.enforce_field_mul(cs, fe_1, fe_2)?
Ok(self.enforce_field_mul(cs, fe_1, fe_2)?)
}
// (ConstrainedValue::GroupElement(group), ConstrainedValue::FieldElement(scalar)) => {
// Ok(Self::evaluate_group_mul(group, scalar))
// }
(ConstrainedValue::Mutable(val_1), val_2) => {
self.enforce_mul_expression(cs, *val_1, val_2)?
self.enforce_mul_expression(cs, *val_1, val_2)
}
(val_1, ConstrainedValue::Mutable(val_2)) => {
self.enforce_mul_expression(cs, val_1, *val_2)?
self.enforce_mul_expression(cs, val_1, *val_2)
}
(ConstrainedValue::Unresolved(string), val_2) => {
let val_1 = ConstrainedValue::from_other(string, &val_2)?;
self.enforce_mul_expression(cs, val_1, val_2)
}
(val_1, ConstrainedValue::Unresolved(string)) => {
let val_2 = ConstrainedValue::from_other(string, &val_1)?;
self.enforce_mul_expression(cs, val_1, val_2)
}
(val_1, val_2) => {
return Err(ExpressionError::IncompatibleTypes(format!(
@ -131,7 +165,7 @@ impl<F: Field + PrimeField, G: Group, CS: ConstraintSystem<F>> ConstrainedProgra
val_1, val_2,
)))
}
})
}
}
fn enforce_div_expression(
@ -140,18 +174,26 @@ impl<F: Field + PrimeField, G: Group, CS: ConstraintSystem<F>> ConstrainedProgra
left: ConstrainedValue<F, G>,
right: ConstrainedValue<F, G>,
) -> Result<ConstrainedValue<F, G>, ExpressionError> {
Ok(match (left, right) {
match (left, right) {
(ConstrainedValue::Integer(num_1), ConstrainedValue::Integer(num_2)) => {
Self::enforce_integer_div(cs, num_1, num_2)?
Ok(Self::enforce_integer_div(cs, num_1, num_2)?)
}
(ConstrainedValue::FieldElement(fe_1), ConstrainedValue::FieldElement(fe_2)) => {
self.enforce_field_div(cs, fe_1, fe_2)?
Ok(self.enforce_field_div(cs, fe_1, fe_2)?)
}
(ConstrainedValue::Mutable(val_1), val_2) => {
self.enforce_div_expression(cs, *val_1, val_2)?
self.enforce_div_expression(cs, *val_1, val_2)
}
(val_1, ConstrainedValue::Mutable(val_2)) => {
self.enforce_div_expression(cs, val_1, *val_2)?
self.enforce_div_expression(cs, val_1, *val_2)
}
(ConstrainedValue::Unresolved(string), val_2) => {
let val_1 = ConstrainedValue::from_other(string, &val_2)?;
self.enforce_div_expression(cs, val_1, val_2)
}
(val_1, ConstrainedValue::Unresolved(string)) => {
let val_2 = ConstrainedValue::from_other(string, &val_1)?;
self.enforce_div_expression(cs, val_1, val_2)
}
(val_1, val_2) => {
return Err(ExpressionError::IncompatibleTypes(format!(
@ -159,7 +201,7 @@ impl<F: Field + PrimeField, G: Group, CS: ConstraintSystem<F>> ConstrainedProgra
val_1, val_2,
)))
}
})
}
}
fn enforce_pow_expression(
&mut self,
@ -167,29 +209,35 @@ impl<F: Field + PrimeField, G: Group, CS: ConstraintSystem<F>> ConstrainedProgra
left: ConstrainedValue<F, G>,
right: ConstrainedValue<F, G>,
) -> Result<ConstrainedValue<F, G>, ExpressionError> {
Ok(match (left, right) {
match (left, right) {
(ConstrainedValue::Integer(num_1), ConstrainedValue::Integer(num_2)) => {
Self::enforce_integer_pow(cs, num_1, num_2)?
Ok(Self::enforce_integer_pow(cs, num_1, num_2)?)
}
(ConstrainedValue::FieldElement(fe_1), ConstrainedValue::Integer(num_2)) => {
self.enforce_field_pow(cs, fe_1, num_2)?
Ok(self.enforce_field_pow(cs, fe_1, num_2)?)
}
(ConstrainedValue::Mutable(val_1), val_2) => {
self.enforce_pow_expression(cs, *val_1, val_2)?
self.enforce_pow_expression(cs, *val_1, val_2)
}
(val_1, ConstrainedValue::Mutable(val_2)) => {
self.enforce_pow_expression(cs, val_1, *val_2)?
self.enforce_pow_expression(cs, val_1, *val_2)
}
(ConstrainedValue::Unresolved(string), val_2) => {
let val_1 = ConstrainedValue::from_other(string, &val_2)?;
self.enforce_pow_expression(cs, val_1, val_2)
}
(val_1, ConstrainedValue::Unresolved(string)) => {
let val_2 = ConstrainedValue::from_other(string, &val_1)?;
self.enforce_pow_expression(cs, val_1, val_2)
}
(_, ConstrainedValue::FieldElement(num_2)) => {
return Err(ExpressionError::InvalidExponent(num_2.to_string()))
Err(ExpressionError::InvalidExponent(num_2.to_string()))
}
(val_1, val_2) => {
return Err(ExpressionError::IncompatibleTypes(format!(
"{} * {}",
val_1, val_2,
)))
}
})
(val_1, val_2) => Err(ExpressionError::IncompatibleTypes(format!(
"{} * {}",
val_1, val_2,
))),
}
}
/// Evaluate Boolean operations
@ -198,32 +246,34 @@ impl<F: Field + PrimeField, G: Group, CS: ConstraintSystem<F>> ConstrainedProgra
left: ConstrainedValue<F, G>,
right: ConstrainedValue<F, G>,
) -> Result<ConstrainedValue<F, G>, ExpressionError> {
Ok(match (left, right) {
match (left, right) {
(ConstrainedValue::Boolean(bool_1), ConstrainedValue::Boolean(bool_2)) => {
Self::boolean_eq(bool_1, bool_2)
Ok(Self::boolean_eq(bool_1, bool_2))
}
(ConstrainedValue::Integer(num_1), ConstrainedValue::Integer(num_2)) => {
Self::evaluate_integer_eq(num_1, num_2)?
Ok(Self::evaluate_integer_eq(num_1, num_2)?)
}
// (ResolvedValue::FieldElement(fe_1), ResolvedValue::FieldElement(fe_2)) => {
// Self::field_eq(fe_1, fe_2)
// }
(ConstrainedValue::GroupElement(ge_1), ConstrainedValue::GroupElement(ge_2)) => {
Self::evaluate_group_eq(ge_1, ge_2)
Ok(Self::evaluate_group_eq(ge_1, ge_2))
}
(ConstrainedValue::Mutable(val_1), val_2) => {
self.evaluate_eq_expression(*val_1, val_2)?
(ConstrainedValue::Mutable(val_1), val_2) => self.evaluate_eq_expression(*val_1, val_2),
(val_1, ConstrainedValue::Mutable(val_2)) => self.evaluate_eq_expression(val_1, *val_2),
(ConstrainedValue::Unresolved(string), val_2) => {
let val_1 = ConstrainedValue::from_other(string, &val_2)?;
self.evaluate_eq_expression(val_1, val_2)
}
(val_1, ConstrainedValue::Mutable(val_2)) => {
self.evaluate_eq_expression(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)
}
(val_1, val_2) => {
return Err(ExpressionError::IncompatibleTypes(format!(
"{} == {}",
val_1, val_2,
)))
}
})
(val_1, val_2) => Err(ExpressionError::IncompatibleTypes(format!(
"{} == {}",
val_1, val_2,
))),
}
}
fn evaluate_geq_expression(
@ -241,6 +291,14 @@ impl<F: Field + PrimeField, G: Group, CS: ConstraintSystem<F>> ConstrainedProgra
(val_1, ConstrainedValue::Mutable(val_2)) => {
self.evaluate_geq_expression(val_1, *val_2)
}
(ConstrainedValue::Unresolved(string), val_2) => {
let val_1 = ConstrainedValue::from_other(string, &val_2)?;
self.evaluate_geq_expression(val_1, val_2)
}
(val_1, ConstrainedValue::Unresolved(string)) => {
let val_2 = ConstrainedValue::from_other(string, &val_1)?;
self.evaluate_geq_expression(val_1, val_2)
}
(val_1, val_2) => Err(ExpressionError::IncompatibleTypes(format!(
"{} >= {}, values must be fields",
val_1, val_2
@ -259,6 +317,14 @@ impl<F: Field + PrimeField, G: Group, CS: ConstraintSystem<F>> ConstrainedProgra
// }
(ConstrainedValue::Mutable(val_1), val_2) => self.evaluate_gt_expression(*val_1, val_2),
(val_1, ConstrainedValue::Mutable(val_2)) => self.evaluate_gt_expression(val_1, *val_2),
(ConstrainedValue::Unresolved(string), val_2) => {
let val_1 = ConstrainedValue::from_other(string, &val_2)?;
self.evaluate_gt_expression(val_1, val_2)
}
(val_1, ConstrainedValue::Unresolved(string)) => {
let val_2 = ConstrainedValue::from_other(string, &val_1)?;
self.evaluate_gt_expression(val_1, val_2)
}
(val_1, val_2) => Err(ExpressionError::IncompatibleTypes(format!(
"{} > {}, values must be fields",
val_1, val_2
@ -281,6 +347,14 @@ impl<F: Field + PrimeField, G: Group, CS: ConstraintSystem<F>> ConstrainedProgra
(val_1, ConstrainedValue::Mutable(val_2)) => {
self.evaluate_leq_expression(val_1, *val_2)
}
(ConstrainedValue::Unresolved(string), val_2) => {
let val_1 = ConstrainedValue::from_other(string, &val_2)?;
self.evaluate_leq_expression(val_1, val_2)
}
(val_1, ConstrainedValue::Unresolved(string)) => {
let val_2 = ConstrainedValue::from_other(string, &val_1)?;
self.evaluate_leq_expression(val_1, val_2)
}
(val_1, val_2) => Err(ExpressionError::IncompatibleTypes(format!(
"{} <= {}, values must be fields",
val_1, val_2
@ -299,6 +373,14 @@ impl<F: Field + PrimeField, G: Group, CS: ConstraintSystem<F>> ConstrainedProgra
// }
(ConstrainedValue::Mutable(val_1), val_2) => self.evaluate_lt_expression(*val_1, val_2),
(val_1, ConstrainedValue::Mutable(val_2)) => self.evaluate_lt_expression(val_1, *val_2),
(ConstrainedValue::Unresolved(string), val_2) => {
let val_1 = ConstrainedValue::from_other(string, &val_2)?;
self.evaluate_lt_expression(val_1, val_2)
}
(val_1, ConstrainedValue::Unresolved(string)) => {
let val_2 = ConstrainedValue::from_other(string, &val_1)?;
self.evaluate_lt_expression(val_1, val_2)
}
(val_1, val_2) => Err(ExpressionError::IncompatibleTypes(format!(
"{} < {}, values must be fields",
val_1, val_2,
@ -306,14 +388,74 @@ impl<F: Field + PrimeField, G: Group, CS: ConstraintSystem<F>> ConstrainedProgra
}
}
/// Enforce ternary conditional expression
fn enforce_conditional_expression(
&mut self,
cs: &mut CS,
file_scope: String,
function_scope: String,
expected_types: Vec<Type<F, G>>,
first: Expression<F, G>,
second: Expression<F, G>,
third: Expression<F, G>,
) -> Result<ConstrainedValue<F, G>, ExpressionError> {
let resolved_first = match self.enforce_expression(
cs,
file_scope.clone(),
function_scope.clone(),
vec![Type::Boolean],
first,
)? {
ConstrainedValue::Boolean(resolved) => resolved,
value => return Err(ExpressionError::IfElseConditional(value.to_string())),
};
let resolved_second = self.enforce_expression(
cs,
file_scope.clone(),
function_scope.clone(),
expected_types.clone(),
second,
)?;
let resolved_third =
self.enforce_expression(cs, file_scope, function_scope, expected_types, third)?;
match (resolved_second, resolved_third) {
(ConstrainedValue::Boolean(bool_2), ConstrainedValue::Boolean(bool_3)) => {
let result = Boolean::conditionally_select(cs, &resolved_first, &bool_2, &bool_3)?;
Ok(ConstrainedValue::Boolean(result))
}
(ConstrainedValue::Integer(integer_2), ConstrainedValue::Integer(integer_3)) => {
let result =
Integer::conditionally_select(cs, &resolved_first, &integer_2, &integer_3)?;
Ok(ConstrainedValue::Integer(result))
}
(_, _) => {
unimplemented!("conditional select gadget not implemented between given types")
}
}
}
/// Enforce array expressions
fn enforce_array_expression(
&mut self,
cs: &mut CS,
file_scope: String,
function_scope: String,
mut expected_types: Vec<Type<F, G>>,
array: Vec<Box<SpreadOrExpression<F, G>>>,
) -> Result<ConstrainedValue<F, G>, ExpressionError> {
// Check explicit array type dimension if given
let expected_dimensions = vec![];
if !expected_types.is_empty() {
match expected_types[0] {
Type::Array(ref _type, ref dimensions) => {
expected_types = vec![expected_types[0].inner_dimension(dimensions)];
}
ref _type => return Err(ExpressionError::IncompatibleTypes(_type.to_string())),
}
}
let mut result = vec![];
for element in array.into_iter() {
match *element {
@ -337,11 +479,23 @@ impl<F: Field + PrimeField, G: Group, CS: ConstraintSystem<F>> ConstrainedProgra
cs,
file_scope.clone(),
function_scope.clone(),
expected_types.clone(),
expression,
)?);
}
}
}
// Check expected_dimensions if given
if !expected_dimensions.is_empty() {
if expected_dimensions[expected_dimensions.len() - 1] != result.len() {
return Err(ExpressionError::InvalidLength(
expected_dimensions[expected_dimensions.len() - 1],
result.len(),
));
}
}
Ok(ConstrainedValue::Array(result))
}
@ -350,10 +504,12 @@ impl<F: Field + PrimeField, G: Group, CS: ConstraintSystem<F>> ConstrainedProgra
cs: &mut CS,
file_scope: String,
function_scope: String,
expected_types: Vec<Type<F, G>>,
index: Expression<F, G>,
) -> Result<usize, ExpressionError> {
match self.enforce_expression(cs, file_scope, function_scope, index)? {
match self.enforce_expression(cs, file_scope, function_scope, expected_types, index)? {
ConstrainedValue::Integer(number) => Ok(number.to_usize()),
ConstrainedValue::Unresolved(string) => Ok(string.parse::<usize>()?),
value => Err(ExpressionError::InvalidIndex(value.to_string())),
}
}
@ -363,6 +519,7 @@ impl<F: Field + PrimeField, G: Group, CS: ConstraintSystem<F>> ConstrainedProgra
cs: &mut CS,
file_scope: String,
function_scope: String,
expected_types: Vec<Type<F, G>>,
array: Box<Expression<F, G>>,
index: RangeOrExpression<F, G>,
) -> Result<ConstrainedValue<F, G>, ExpressionError> {
@ -370,6 +527,7 @@ impl<F: Field + PrimeField, G: Group, CS: ConstraintSystem<F>> ConstrainedProgra
cs,
file_scope.clone(),
function_scope.clone(),
expected_types.clone(),
*array,
)? {
ConstrainedValue::Array(array) => array,
@ -395,7 +553,8 @@ impl<F: Field + PrimeField, G: Group, CS: ConstraintSystem<F>> ConstrainedProgra
))
}
RangeOrExpression::Expression(index) => {
let index_resolved = self.enforce_index(cs, file_scope, function_scope, index)?;
let index_resolved =
self.enforce_index(cs, file_scope, function_scope, expected_types, index)?;
Ok(array[index_resolved].to_owned())
}
}
@ -434,12 +593,10 @@ impl<F: Field + PrimeField, G: Group, CS: ConstraintSystem<F>> ConstrainedProgra
cs,
file_scope.clone(),
function_scope.clone(),
vec![_type.clone()],
field.expression,
)?;
// Check field type
field_value.expect_type(&_type)?;
resolved_members
.push(ConstrainedCircuitMember(identifier, field_value))
}
@ -482,6 +639,7 @@ impl<F: Field + PrimeField, G: Group, CS: ConstraintSystem<F>> ConstrainedProgra
cs: &mut CS,
file_scope: String,
function_scope: String,
expected_types: Vec<Type<F, G>>,
circuit_identifier: Box<Expression<F, G>>,
circuit_member: Identifier<F, G>,
) -> Result<ConstrainedValue<F, G>, ExpressionError> {
@ -489,6 +647,7 @@ impl<F: Field + PrimeField, G: Group, CS: ConstraintSystem<F>> ConstrainedProgra
cs,
file_scope.clone(),
function_scope.clone(),
expected_types.clone(),
*circuit_identifier.clone(),
)? {
ConstrainedValue::CircuitExpression(name, members) => (name, members),
@ -541,6 +700,7 @@ impl<F: Field + PrimeField, G: Group, CS: ConstraintSystem<F>> ConstrainedProgra
cs: &mut CS,
file_scope: String,
function_scope: String,
expected_types: Vec<Type<F, G>>,
circuit_identifier: Box<Expression<F, G>>,
circuit_member: Identifier<F, G>,
) -> Result<ConstrainedValue<F, G>, ExpressionError> {
@ -549,6 +709,7 @@ impl<F: Field + PrimeField, G: Group, CS: ConstraintSystem<F>> ConstrainedProgra
cs,
file_scope.clone(),
function_scope.clone(),
expected_types.clone(),
*circuit_identifier.clone(),
)? {
ConstrainedValue::CircuitDefinition(circuit_definition) => circuit_definition,
@ -591,6 +752,7 @@ impl<F: Field + PrimeField, G: Group, CS: ConstraintSystem<F>> ConstrainedProgra
cs: &mut CS,
file_scope: String,
function_scope: String,
expected_types: Vec<Type<F, G>>,
function: Box<Expression<F, G>>,
arguments: Vec<Expression<F, G>>,
) -> Result<ConstrainedValue<F, G>, ExpressionError> {
@ -598,6 +760,7 @@ impl<F: Field + PrimeField, G: Group, CS: ConstraintSystem<F>> ConstrainedProgra
cs,
file_scope.clone(),
function_scope.clone(),
expected_types.clone(),
*function.clone(),
)?;
@ -627,81 +790,127 @@ impl<F: Field + PrimeField, G: Group, CS: ConstraintSystem<F>> ConstrainedProgra
}
}
pub(crate) fn enforce_number_implicit(
expected_types: Vec<Type<F, G>>,
value: String,
) -> Result<ConstrainedValue<F, G>, ExpressionError> {
if expected_types.len() == 1 {
return Ok(ConstrainedValue::from_type(value, &expected_types[0])?);
}
Ok(ConstrainedValue::Unresolved(value))
}
pub(crate) fn enforce_expression(
&mut self,
cs: &mut CS,
file_scope: String,
function_scope: String,
expected_types: Vec<Type<F, G>>,
expression: Expression<F, G>,
) -> Result<ConstrainedValue<F, G>, ExpressionError> {
match expression {
// Variables
Expression::Identifier(unresolved_variable) => {
self.evaluate_identifier(file_scope, function_scope, unresolved_variable)
}
Expression::Identifier(unresolved_variable) => self.evaluate_identifier(
file_scope,
function_scope,
expected_types,
unresolved_variable,
),
// Values
Expression::Integer(integer) => Ok(Self::get_integer_constant(integer)),
Expression::FieldElement(fe) => Ok(Self::get_field_element_constant(fe)),
Expression::GroupElement(gr) => Ok(ConstrainedValue::GroupElement(gr)),
Expression::Boolean(bool) => Ok(Self::get_boolean_constant(bool)),
Expression::Implicit(value) => Self::enforce_number_implicit(expected_types, value),
// Binary operations
Expression::Add(left, right) => {
let resolved_left =
self.enforce_expression(cs, file_scope.clone(), function_scope.clone(), *left)?;
let resolved_left = self.enforce_expression(
cs,
file_scope.clone(),
function_scope.clone(),
expected_types.clone(),
*left,
)?;
let resolved_right = self.enforce_expression(
cs,
file_scope.clone(),
function_scope.clone(),
expected_types,
*right,
)?;
self.enforce_add_expression(cs, resolved_left, resolved_right)
}
Expression::Sub(left, right) => {
let resolved_left =
self.enforce_expression(cs, file_scope.clone(), function_scope.clone(), *left)?;
let resolved_left = self.enforce_expression(
cs,
file_scope.clone(),
function_scope.clone(),
expected_types.clone(),
*left,
)?;
let resolved_right = self.enforce_expression(
cs,
file_scope.clone(),
function_scope.clone(),
expected_types,
*right,
)?;
self.enforce_sub_expression(cs, resolved_left, resolved_right)
}
Expression::Mul(left, right) => {
let resolved_left =
self.enforce_expression(cs, file_scope.clone(), function_scope.clone(), *left)?;
let resolved_left = self.enforce_expression(
cs,
file_scope.clone(),
function_scope.clone(),
expected_types.clone(),
*left,
)?;
let resolved_right = self.enforce_expression(
cs,
file_scope.clone(),
function_scope.clone(),
expected_types,
*right,
)?;
self.enforce_mul_expression(cs, resolved_left, resolved_right)
}
Expression::Div(left, right) => {
let resolved_left =
self.enforce_expression(cs, file_scope.clone(), function_scope.clone(), *left)?;
let resolved_left = self.enforce_expression(
cs,
file_scope.clone(),
function_scope.clone(),
expected_types.clone(),
*left,
)?;
let resolved_right = self.enforce_expression(
cs,
file_scope.clone(),
function_scope.clone(),
expected_types,
*right,
)?;
self.enforce_div_expression(cs, resolved_left, resolved_right)
}
Expression::Pow(left, right) => {
let resolved_left =
self.enforce_expression(cs, file_scope.clone(), function_scope.clone(), *left)?;
let resolved_left = self.enforce_expression(
cs,
file_scope.clone(),
function_scope.clone(),
expected_types.clone(),
*left,
)?;
let resolved_right = self.enforce_expression(
cs,
file_scope.clone(),
function_scope.clone(),
expected_types,
*right,
)?;
@ -713,87 +922,130 @@ impl<F: Field + PrimeField, G: Group, CS: ConstraintSystem<F>> ConstrainedProgra
cs,
file_scope,
function_scope,
expected_types,
*expression,
)?)?),
Expression::Or(left, right) => {
let resolved_left =
self.enforce_expression(cs, file_scope.clone(), function_scope.clone(), *left)?;
let resolved_left = self.enforce_expression(
cs,
file_scope.clone(),
function_scope.clone(),
expected_types.clone(),
*left,
)?;
let resolved_right = self.enforce_expression(
cs,
file_scope.clone(),
function_scope.clone(),
expected_types,
*right,
)?;
Ok(self.enforce_or(cs, resolved_left, resolved_right)?)
}
Expression::And(left, right) => {
let resolved_left =
self.enforce_expression(cs, file_scope.clone(), function_scope.clone(), *left)?;
let resolved_left = self.enforce_expression(
cs,
file_scope.clone(),
function_scope.clone(),
expected_types.clone(),
*left,
)?;
let resolved_right = self.enforce_expression(
cs,
file_scope.clone(),
function_scope.clone(),
expected_types,
*right,
)?;
Ok(self.enforce_and(cs, resolved_left, resolved_right)?)
}
Expression::Eq(left, right) => {
let resolved_left =
self.enforce_expression(cs, file_scope.clone(), function_scope.clone(), *left)?;
let resolved_left = self.enforce_expression(
cs,
file_scope.clone(),
function_scope.clone(),
expected_types.clone(),
*left,
)?;
let resolved_right = self.enforce_expression(
cs,
file_scope.clone(),
function_scope.clone(),
expected_types,
*right,
)?;
Ok(self.evaluate_eq_expression(resolved_left, resolved_right)?)
}
Expression::Geq(left, right) => {
let resolved_left =
self.enforce_expression(cs, file_scope.clone(), function_scope.clone(), *left)?;
let resolved_left = self.enforce_expression(
cs,
file_scope.clone(),
function_scope.clone(),
expected_types.clone(),
*left,
)?;
let resolved_right = self.enforce_expression(
cs,
file_scope.clone(),
function_scope.clone(),
expected_types,
*right,
)?;
Ok(self.evaluate_geq_expression(resolved_left, resolved_right)?)
}
Expression::Gt(left, right) => {
let resolved_left =
self.enforce_expression(cs, file_scope.clone(), function_scope.clone(), *left)?;
let resolved_left = self.enforce_expression(
cs,
file_scope.clone(),
function_scope.clone(),
expected_types.clone(),
*left,
)?;
let resolved_right = self.enforce_expression(
cs,
file_scope.clone(),
function_scope.clone(),
expected_types,
*right,
)?;
Ok(self.evaluate_gt_expression(resolved_left, resolved_right)?)
}
Expression::Leq(left, right) => {
let resolved_left =
self.enforce_expression(cs, file_scope.clone(), function_scope.clone(), *left)?;
let resolved_left = self.enforce_expression(
cs,
file_scope.clone(),
function_scope.clone(),
expected_types.clone(),
*left,
)?;
let resolved_right = self.enforce_expression(
cs,
file_scope.clone(),
function_scope.clone(),
expected_types,
*right,
)?;
Ok(self.evaluate_leq_expression(resolved_left, resolved_right)?)
}
Expression::Lt(left, right) => {
let resolved_left =
self.enforce_expression(cs, file_scope.clone(), function_scope.clone(), *left)?;
let resolved_left = self.enforce_expression(
cs,
file_scope.clone(),
function_scope.clone(),
expected_types.clone(),
*left,
)?;
let resolved_right = self.enforce_expression(
cs,
file_scope.clone(),
function_scope.clone(),
expected_types,
*right,
)?;
@ -801,31 +1053,28 @@ impl<F: Field + PrimeField, G: Group, CS: ConstraintSystem<F>> ConstrainedProgra
}
// Conditionals
Expression::IfElse(first, second, third) => {
let resolved_first = match self.enforce_expression(
cs,
file_scope.clone(),
function_scope.clone(),
*first,
)? {
ConstrainedValue::Boolean(resolved) => resolved,
value => return Err(ExpressionError::IfElseConditional(value.to_string())),
};
if resolved_first.eq(&Boolean::Constant(true)) {
self.enforce_expression(cs, file_scope, function_scope, *second)
} else {
self.enforce_expression(cs, file_scope, function_scope, *third)
}
}
Expression::IfElse(first, second, third) => self.enforce_conditional_expression(
cs,
file_scope,
function_scope,
expected_types,
*first,
*second,
*third,
),
// Arrays
Expression::Array(array) => {
self.enforce_array_expression(cs, file_scope, function_scope, array)
}
Expression::ArrayAccess(array, index) => {
self.enforce_array_access_expression(cs, file_scope, function_scope, array, *index)
self.enforce_array_expression(cs, file_scope, function_scope, expected_types, array)
}
Expression::ArrayAccess(array, index) => self.enforce_array_access_expression(
cs,
file_scope,
function_scope,
expected_types,
array,
*index,
),
// Circuits
Expression::Circuit(circuit_name, members) => self.enforce_circuit_expression(
@ -840,6 +1089,7 @@ impl<F: Field + PrimeField, G: Group, CS: ConstraintSystem<F>> ConstrainedProgra
cs,
file_scope,
function_scope,
expected_types,
circuit_variable,
circuit_member,
),
@ -848,6 +1098,7 @@ impl<F: Field + PrimeField, G: Group, CS: ConstraintSystem<F>> ConstrainedProgra
cs,
file_scope,
function_scope,
expected_types,
circuit_identifier,
circuit_member,
),
@ -857,6 +1108,7 @@ impl<F: Field + PrimeField, G: Group, CS: ConstraintSystem<F>> ConstrainedProgra
cs,
file_scope,
function_scope,
expected_types,
function,
arguments,
),

View File

@ -13,10 +13,10 @@ use snarkos_models::{
};
impl<F: Field + PrimeField, G: Group, CS: ConstraintSystem<F>> ConstrainedProgram<F, G, CS> {
fn check_inputs_length(expected: usize, actual: usize) -> Result<(), FunctionError> {
fn check_arguments_length(expected: usize, actual: usize) -> Result<(), FunctionError> {
// Make sure we are given the correct number of arguments
if expected != actual {
Err(FunctionError::InputsLength(expected, actual))
Err(FunctionError::ArgumentsLength(expected, actual))
} else {
Ok(())
}
@ -28,13 +28,21 @@ impl<F: Field + PrimeField, G: Group, CS: ConstraintSystem<F>> ConstrainedProgra
scope: String,
caller_scope: String,
function_name: String,
expected_types: Vec<Type<F, G>>,
input: Expression<F, G>,
) -> Result<ConstrainedValue<F, G>, FunctionError> {
// Evaluate the function input value as pass by value from the caller or
// evaluate as an expression in the current function scope
match input {
Expression::Identifier(identifier) => {
Ok(self.evaluate_identifier(caller_scope, function_name, identifier)?)
Expression::Identifier(identifier) => Ok(self.evaluate_identifier(
caller_scope,
function_name,
expected_types,
identifier,
)?),
expression => {
Ok(self.enforce_expression(cs, scope, function_name, expected_types, expression)?)
}
expression => Ok(self.enforce_expression(cs, scope, function_name, expression)?),
}
}
@ -49,7 +57,7 @@ impl<F: Field + PrimeField, G: Group, CS: ConstraintSystem<F>> ConstrainedProgra
let function_name = new_scope(scope.clone(), function.get_name());
// Make sure we are given the correct number of inputs
Self::check_inputs_length(function.inputs.len(), inputs.len())?;
Self::check_arguments_length(function.inputs.len(), inputs.len())?;
// Store input values as new variables in resolved program
for (input_model, input_expression) in
@ -61,12 +69,10 @@ impl<F: Field + PrimeField, G: Group, CS: ConstraintSystem<F>> ConstrainedProgra
scope.clone(),
caller_scope.clone(),
function_name.clone(),
vec![input_model._type.clone()],
input_expression,
)?;
// Check that input is correct type
input_value.expect_type(&input_model._type)?;
if input_model.mutable {
input_value = ConstrainedValue::Mutable(Box::new(input_value))
}
@ -94,6 +100,10 @@ impl<F: Field + PrimeField, G: Group, CS: ConstraintSystem<F>> ConstrainedProgra
}
}
if let ConstrainedValue::Return(ref returns) = return_values {
Self::check_arguments_length(function.returns.len(), returns.len())?;
}
Ok(return_values)
}
@ -112,12 +122,12 @@ impl<F: Field + PrimeField, G: Group, CS: ConstraintSystem<F>> ConstrainedProgra
match input_value {
Some(InputValue::Array(arr)) => {
// Check the dimension of the array
Self::check_inputs_length(expected_length, arr.len())?;
Self::check_arguments_length(expected_length, arr.len())?;
// Allocate each value in the current row
for (i, value) in arr.into_iter().enumerate() {
let value_name = new_scope(name.clone(), i.to_string());
let value_type = array_type.next_dimension(&array_dimensions);
let value_type = array_type.outer_dimension(&array_dimensions);
array_value.push(self.allocate_main_function_input(
cs,
@ -132,7 +142,7 @@ impl<F: Field + PrimeField, G: Group, CS: ConstraintSystem<F>> ConstrainedProgra
// Allocate all row values as none
for i in 0..expected_length {
let value_name = new_scope(name.clone(), i.to_string());
let value_type = array_type.next_dimension(&array_dimensions);
let value_type = array_type.outer_dimension(&array_dimensions);
array_value.push(
self.allocate_main_function_input(
@ -184,7 +194,7 @@ impl<F: Field + PrimeField, G: Group, CS: ConstraintSystem<F>> ConstrainedProgra
let function_name = new_scope(scope.clone(), function.get_name());
// Make sure we are given the correct number of inputs
Self::check_inputs_length(function.inputs.len(), inputs.len())?;
Self::check_arguments_length(function.inputs.len(), inputs.len())?;
// Iterate over main function inputs and allocate new passed-by variable values
let mut input_variables = vec![];

View File

@ -17,4 +17,8 @@ impl<F: Field + PrimeField, G: Group, CS: ConstraintSystem<F>> ConstrainedProgra
pub fn evaluate_group_sub(group_element_1: G, group_element_2: G) -> ConstrainedValue<F, G> {
ConstrainedValue::GroupElement(group_element_1.sub(&group_element_2))
}
//
// pub fn evaluate_group_mul(group_element: G, scalar_field: G::ScalarField) -> ConstrainedValue<F, G> {
// ConstrainedValue::GroupElement(group_element.mul(&scalar_field))
// }
}

View File

@ -7,11 +7,72 @@ use crate::{
IntegerType,
};
use snarkos_errors::gadgets::SynthesisError;
use snarkos_models::{
curves::{Field, Group, PrimeField},
gadgets::{r1cs::ConstraintSystem, utilities::boolean::Boolean},
gadgets::{
r1cs::ConstraintSystem,
utilities::{
boolean::Boolean, select::CondSelectGadget, uint128::UInt128, uint16::UInt16,
uint32::UInt32, uint64::UInt64, uint8::UInt8,
},
},
};
impl Integer {
pub fn to_usize(&self) -> usize {
match self {
Integer::U8(u8) => u8.value.unwrap() as usize,
Integer::U16(u16) => u16.value.unwrap() as usize,
Integer::U32(u32) => u32.value.unwrap() as usize,
Integer::U64(u64) => u64.value.unwrap() as usize,
Integer::U128(u128) => u128.value.unwrap() as usize,
}
}
pub(crate) fn get_type(&self) -> IntegerType {
match self {
Integer::U8(_u8) => IntegerType::U8,
Integer::U16(_u16) => IntegerType::U16,
Integer::U32(_u32) => IntegerType::U32,
Integer::U64(_u64) => IntegerType::U64,
Integer::U128(_u128) => IntegerType::U128,
}
}
}
impl<F: Field + PrimeField> CondSelectGadget<F> for Integer {
fn conditionally_select<CS: ConstraintSystem<F>>(
cs: CS,
cond: &Boolean,
first: &Self,
second: &Self,
) -> Result<Self, SynthesisError> {
match (first, second) {
(Integer::U8(u8_first), Integer::U8(u8_second)) => Ok(Integer::U8(
UInt8::conditionally_select(cs, cond, u8_first, u8_second)?,
)),
(Integer::U16(u16_first), Integer::U16(u18_second)) => Ok(Integer::U16(
UInt16::conditionally_select(cs, cond, u16_first, u18_second)?,
)),
(Integer::U32(u32_first), Integer::U32(u32_second)) => Ok(Integer::U32(
UInt32::conditionally_select(cs, cond, u32_first, u32_second)?,
)),
(Integer::U64(u64_first), Integer::U64(u64_second)) => Ok(Integer::U64(
UInt64::conditionally_select(cs, cond, u64_first, u64_second)?,
)),
(Integer::U128(u128_first), Integer::U128(u128_second)) => Ok(Integer::U128(
UInt128::conditionally_select(cs, cond, u128_first, u128_second)?,
)),
(_, _) => Err(SynthesisError::Unsatisfiable), // types do not match
}
}
fn cost() -> usize {
unimplemented!("Cannot calculate cost.")
}
}
impl<F: Field + PrimeField, G: Group, CS: ConstraintSystem<F>> ConstrainedProgram<F, G, CS> {
pub(crate) fn get_integer_constant(integer: Integer) -> ConstrainedValue<F, G> {
ConstrainedValue::Integer(integer)

View File

@ -53,8 +53,13 @@ impl<F: Field + PrimeField, G: Group, CS: ConstraintSystem<F>> ConstrainedProgra
// 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) => {
let index =
self.enforce_index(cs, file_scope.clone(), function_scope.clone(), index)?;
let index = self.enforce_index(
cs,
file_scope.clone(),
function_scope.clone(),
vec![],
index,
)?;
// Modify the single value of the array in place
match self.get_mutable_assignee(name)? {
@ -136,8 +141,13 @@ impl<F: Field + PrimeField, G: Group, CS: ConstraintSystem<F>> ConstrainedProgra
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(), expression)?;
let new_value = self.enforce_expression(
cs,
file_scope.clone(),
function_scope.clone(),
vec![],
expression,
)?;
// Mutate the old value into the new value
match assignee {
@ -168,11 +178,6 @@ impl<F: Field + PrimeField, G: Group, CS: ConstraintSystem<F>> ConstrainedProgra
variable: Variable<F, G>,
mut value: ConstrainedValue<F, G>,
) -> Result<(), StatementError> {
// Check optional explicit type
if let Some(_type) = variable._type {
value.expect_type(&_type)?;
}
// Store with given mutability
if variable.mutable {
value = ConstrainedValue::Mutable(Box::new(value));
@ -193,8 +198,17 @@ impl<F: Field + PrimeField, G: Group, CS: ConstraintSystem<F>> ConstrainedProgra
variable: Variable<F, G>,
expression: Expression<F, G>,
) -> Result<(), StatementError> {
let value =
self.enforce_expression(cs, file_scope.clone(), function_scope.clone(), expression)?;
let mut expected_types = vec![];
if let Some(ref _type) = variable._type {
expected_types.push(_type.clone());
}
let value = self.enforce_expression(
cs,
file_scope.clone(),
function_scope.clone(),
expected_types,
expression,
)?;
self.store_definition(function_scope, variable, value)
}
@ -207,11 +221,19 @@ impl<F: Field + PrimeField, G: Group, CS: ConstraintSystem<F>> ConstrainedProgra
variables: Vec<Variable<F, G>>,
function: Expression<F, G>,
) -> Result<(), StatementError> {
let mut expected_types = vec![];
for variable in variables.iter() {
if let Some(ref _type) = variable._type {
expected_types.push(_type.clone());
}
}
// Expect return values from function
let return_values = match self.enforce_expression(
cs,
file_scope.clone(),
function_scope.clone(),
expected_types,
function,
)? {
ConstrainedValue::Return(values) => values,
@ -252,13 +274,14 @@ impl<F: Field + PrimeField, G: Group, CS: ConstraintSystem<F>> ConstrainedProgra
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_expression(
cs,
file_scope.clone(),
function_scope.clone(),
expected_types,
expression,
)?;
result.expect_type(&ty)?;
returns.push(result);
}
@ -300,10 +323,12 @@ impl<F: Field + PrimeField, G: Group, CS: ConstraintSystem<F>> ConstrainedProgra
statement: ConditionalStatement<F, G>,
return_types: Vec<Type<F, G>>,
) -> Result<Option<ConstrainedValue<F, G>>, StatementError> {
let expected_types = vec![Type::Boolean];
let condition = match self.enforce_expression(
cs,
file_scope.clone(),
function_scope.clone(),
expected_types,
statement.condition.clone(),
)? {
ConstrainedValue::Boolean(resolved) => resolved,
@ -487,15 +512,31 @@ impl<F: Field + PrimeField, G: Group, CS: ConstraintSystem<F>> ConstrainedProgra
}
}
Statement::AssertEq(left, right) => {
let resolved_left =
self.enforce_expression(cs, file_scope.clone(), function_scope.clone(), left)?;
let resolved_right =
self.enforce_expression(cs, file_scope.clone(), function_scope.clone(), 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,
)?;
self.enforce_assert_eq_statement(cs, resolved_left, resolved_right)?;
}
Statement::Expression(expression) => {
match self.enforce_expression(cs, file_scope, function_scope, expression.clone())? {
match self.enforce_expression(
cs,
file_scope,
function_scope,
vec![],
expression.clone(),
)? {
ConstrainedValue::Return(values) => {
if !values.is_empty() {
return Err(StatementError::Unassigned(expression.to_string()));

View File

@ -2,13 +2,15 @@
use crate::{
errors::ValueError,
types::{Circuit, FieldElement, Function, Identifier, Type},
Integer,
types::{Circuit, FieldElement, Function, Identifier, Integer, IntegerType, Type},
};
use snarkos_models::{
curves::{Field, Group, PrimeField},
gadgets::utilities::boolean::Boolean,
gadgets::utilities::{
boolean::Boolean, uint128::UInt128, uint16::UInt16, uint32::UInt32, uint64::UInt64,
uint8::UInt8,
},
};
use std::fmt;
@ -35,76 +37,54 @@ pub enum ConstrainedValue<F: Field + PrimeField, G: Group> {
Mutable(Box<ConstrainedValue<F, G>>),
Static(Box<ConstrainedValue<F, G>>),
Unresolved(String),
}
impl<F: Field + PrimeField, G: Group> ConstrainedValue<F, G> {
pub(crate) fn expect_type(&self, _type: &Type<F, G>) -> Result<(), ValueError> {
match (self, _type) {
(ConstrainedValue::Integer(ref integer), Type::IntegerType(ref _type)) => {
integer.expect_type(_type)?;
}
(ConstrainedValue::FieldElement(ref _f), Type::FieldElement) => {}
(ConstrainedValue::GroupElement(ref _g), Type::GroupElement) => {}
(ConstrainedValue::Boolean(ref _b), Type::Boolean) => {}
(ConstrainedValue::Array(ref arr), Type::Array(ref _type, ref dimensions)) => {
// check array lengths are equal
if arr.len() != dimensions[0] {
return Err(ValueError::ArrayLength(format!(
"Expected array {:?} to be length {:?}",
arr, dimensions[0]
)));
}
pub(crate) fn from_other(
value: String,
other: &ConstrainedValue<F, G>,
) -> Result<Self, ValueError> {
let other_type = other.to_type();
// get next dimension of array if nested
let next_type = _type.next_dimension(dimensions);
ConstrainedValue::from_type(value, &other_type)
}
// check each value in array matches
for value in arr {
value.expect_type(&next_type)?;
}
}
(
ConstrainedValue::CircuitExpression(ref actual_name, ref _members),
Type::Circuit(ref expected_name),
) => {
if expected_name != actual_name {
return Err(ValueError::CircuitName(
expected_name.to_string(),
actual_name.to_string(),
));
}
}
(
ConstrainedValue::CircuitExpression(ref actual_name, ref _members),
Type::SelfType,
) => {
if Identifier::new("Self".into()) == *actual_name {
return Err(ValueError::CircuitName(
"Self".into(),
actual_name.to_string(),
));
}
}
(ConstrainedValue::Return(ref values), _type) => {
for value in values {
value.expect_type(_type)?;
}
}
(ConstrainedValue::Mutable(ref value), _type) => {
value.expect_type(&_type)?;
}
(ConstrainedValue::Static(ref value), _type) => {
value.expect_type(&_type)?;
}
(value, _type) => {
return Err(ValueError::TypeError(format!(
"expected type {}, got {}",
_type, value
)))
}
pub(crate) fn from_type(value: String, _type: &Type<F, G>) -> Result<Self, ValueError> {
match _type {
Type::IntegerType(integer_type) => Ok(ConstrainedValue::Integer(match integer_type {
IntegerType::U8 => Integer::U8(UInt8::constant(value.parse::<u8>()?)),
IntegerType::U16 => Integer::U16(UInt16::constant(value.parse::<u16>()?)),
IntegerType::U32 => Integer::U32(UInt32::constant(value.parse::<u32>()?)),
IntegerType::U64 => Integer::U64(UInt64::constant(value.parse::<u64>()?)),
IntegerType::U128 => Integer::U128(UInt128::constant(value.parse::<u128>()?)),
})),
Type::FieldElement => Ok(ConstrainedValue::FieldElement(FieldElement::Constant(
F::from_str(&value).unwrap_or_default(),
))),
Type::GroupElement => Ok(ConstrainedValue::GroupElement({
use std::str::FromStr;
let scalar = G::ScalarField::from_str(&value).unwrap_or_default();
let point = G::default().mul(&scalar);
point
})),
Type::Boolean => Ok(ConstrainedValue::Boolean(Boolean::Constant(
value.parse::<bool>()?,
))),
Type::Array(ref _type, _dimensions) => ConstrainedValue::from_type(value, _type),
_ => Ok(ConstrainedValue::Unresolved(value)),
}
}
Ok(())
pub(crate) fn to_type(&self) -> Type<F, G> {
match self {
ConstrainedValue::Integer(integer) => Type::IntegerType(integer.get_type()),
ConstrainedValue::FieldElement(_field) => Type::FieldElement,
ConstrainedValue::GroupElement(_group) => Type::GroupElement,
ConstrainedValue::Boolean(_bool) => Type::Boolean,
_ => unimplemented!("to type only implemented for primitives"),
}
}
}
@ -153,6 +133,7 @@ impl<F: Field + PrimeField, G: Group> fmt::Display for ConstrainedValue<F, G> {
}
ConstrainedValue::Mutable(ref value) => write!(f, "mut {}", value),
ConstrainedValue::Static(ref value) => write!(f, "static {}", value),
ConstrainedValue::Unresolved(ref value) => write!(f, "unresolved {}", value),
}
}
}

View File

@ -1,5 +1,8 @@
use crate::errors::{BooleanError, FieldElementError, FunctionError, IntegerError, ValueError};
use snarkos_errors::gadgets::SynthesisError;
use std::num::ParseIntError;
#[derive(Debug, Error)]
pub enum ExpressionError {
// Identifiers
@ -7,6 +10,9 @@ pub enum ExpressionError {
UndefinedIdentifier(String),
// Types
#[error("Expected single type for implicit number {}", _0)]
SingleType(String),
#[error("{}", _0)]
IncompatibleTypes(String),
@ -16,6 +22,9 @@ pub enum ExpressionError {
#[error("{}", _0)]
IntegerError(IntegerError),
#[error("{}", _0)]
ParseIntError(ParseIntError),
#[error("{}", _0)]
FieldElementError(FieldElementError),
@ -41,6 +50,9 @@ pub enum ExpressionError {
#[error("Index must resolve to an integer, got {}", _0)]
InvalidIndex(String),
#[error("Expected array length {}, got {}", _0, _1)]
InvalidLength(usize, usize),
// Circuits
#[error(
"Circuit {} must be declared before it is used in an inline expression",
@ -82,6 +94,15 @@ pub enum ExpressionError {
// Conditionals
#[error("If, else conditional must resolve to a boolean, got {}", _0)]
IfElseConditional(String),
#[error("{}", _0)]
SynthesisError(SynthesisError),
}
impl From<SynthesisError> for ExpressionError {
fn from(error: SynthesisError) -> Self {
ExpressionError::SynthesisError(error)
}
}
impl From<ValueError> for ExpressionError {
@ -96,6 +117,12 @@ impl From<IntegerError> for ExpressionError {
}
}
impl From<ParseIntError> for ExpressionError {
fn from(error: ParseIntError) -> Self {
ExpressionError::ParseIntError(error)
}
}
impl From<FieldElementError> for ExpressionError {
fn from(error: FieldElementError) -> Self {
ExpressionError::FieldElementError(error)

View File

@ -5,7 +5,7 @@ use crate::errors::{
#[derive(Debug, Error)]
pub enum FunctionError {
#[error("Function expected {} inputs, got {}", _0, _1)]
InputsLength(usize, usize),
ArgumentsLength(usize, usize),
#[error("Function input type not defined {}", _0)]
UndefinedInput(String),

View File

@ -1,7 +1,18 @@
use crate::errors::IntegerError;
use std::num::ParseIntError;
use std::str::ParseBoolError;
#[derive(Debug, Error)]
pub enum ValueError {
#[error("{}", _0)]
ParseIntError(ParseIntError),
#[error("{}", _0)]
ParseBoolError(ParseBoolError),
#[error("{}", _0)]
IntegerError(IntegerError),
/// Unexpected array length
#[error("{}", _0)]
ArrayLength(String),
@ -9,9 +20,6 @@ pub enum ValueError {
#[error("Expected type array, got {}", _0)]
ArrayModel(String),
#[error("{}", _0)]
IntegerError(IntegerError),
/// Unexpected circuit name
#[error("Expected circuit name {} got {}", _0, _1)]
CircuitName(String, String),
@ -21,6 +29,18 @@ pub enum ValueError {
TypeError(String),
}
impl From<ParseIntError> for ValueError {
fn from(error: ParseIntError) -> Self {
ValueError::ParseIntError(error)
}
}
impl From<ParseBoolError> for ValueError {
fn from(error: ParseBoolError) -> Self {
ValueError::ParseBoolError(error)
}
}
impl From<IntegerError> for ValueError {
fn from(error: IntegerError) -> Self {
ValueError::IntegerError(error)

View File

@ -89,11 +89,12 @@ type_list = _{(_type ~ ("," ~ _type)*)?}
/// Values
value_number = @{ "0" | ASCII_NONZERO_DIGIT ~ ASCII_DIGIT* }
value_integer = { value_number ~ type_integer? }
value_implicit = { value_number }
value_integer = { value_number ~ type_integer }
value_field = { value_number ~ type_field }
value_group = { value_number ~ type_group }
value_boolean = { "true" | "false" }
value = { value_field | value_group | value_boolean | value_integer }
value = { value_field | value_group | value_boolean | value_integer | value_implicit }
expression_primitive = { value | identifier }
/// Variables + Mutability

View File

@ -1,7 +1,7 @@
//! A typed Leo program consists of import, circuit, and function definitions.
//! Each defined type consists of typed statements and expressions.
use crate::{errors::IntegerError, Import};
use crate::Import;
use snarkos_models::curves::{Field, Group, PrimeField};
use snarkos_models::gadgets::{
@ -54,40 +54,6 @@ pub enum Integer {
U128(UInt128),
}
impl Integer {
pub fn to_usize(&self) -> usize {
match self {
Integer::U8(u8) => u8.value.unwrap() as usize,
Integer::U16(u16) => u16.value.unwrap() as usize,
Integer::U32(u32) => u32.value.unwrap() as usize,
Integer::U64(u64) => u64.value.unwrap() as usize,
Integer::U128(u128) => u128.value.unwrap() as usize,
}
}
pub(crate) fn get_type(&self) -> IntegerType {
match self {
Integer::U8(_u8) => IntegerType::U8,
Integer::U16(_u16) => IntegerType::U16,
Integer::U32(_u32) => IntegerType::U32,
Integer::U64(_u64) => IntegerType::U64,
Integer::U128(_u128) => IntegerType::U128,
}
}
pub(crate) fn expect_type(&self, integer_type: &IntegerType) -> Result<(), IntegerError> {
if self.get_type() != *integer_type {
unimplemented!(
"expected integer type {}, got {}",
self.get_type(),
integer_type
)
}
Ok(())
}
}
/// A constant or allocated element in the field
#[derive(Clone, PartialEq, Eq)]
pub enum FieldElement<F: Field + PrimeField> {
@ -95,13 +61,6 @@ pub enum FieldElement<F: Field + PrimeField> {
Allocated(Option<F>, R1CSVariable),
}
// /// A constant or allocated element in the field
// #[derive(Clone, PartialEq, Eq)]
// pub enum GroupElement<G: Field + PrimeField> {
// Constant(G),
// Allocated(Option<G>, R1CSVariable),
// }
/// Range or expression enum
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum RangeOrExpression<F: Field + PrimeField, G: Group> {
@ -127,6 +86,7 @@ pub enum Expression<F: Field + PrimeField, G: Group> {
FieldElement(FieldElement<F>),
GroupElement(G),
Boolean(Boolean),
Implicit(String),
// Number operations
Add(Box<Expression<F, G>>, Box<Expression<F, G>>),
@ -196,7 +156,7 @@ pub enum Type<F: Field + PrimeField, G: Group> {
}
impl<F: Field + PrimeField, G: Group> Type<F, G> {
pub fn next_dimension(&self, dimensions: &Vec<usize>) -> Self {
pub fn outer_dimension(&self, dimensions: &Vec<usize>) -> Self {
let _type = self.clone();
if dimensions.len() > 1 {
@ -208,6 +168,19 @@ impl<F: Field + PrimeField, G: Group> Type<F, G> {
_type
}
pub fn inner_dimension(&self, dimensions: &Vec<usize>) -> Self {
let _type = self.clone();
if dimensions.len() > 1 {
let mut next = vec![];
next.extend_from_slice(&dimensions[..dimensions.len() - 1]);
return Type::Array(Box::new(_type), next);
}
_type
}
}
#[derive(Clone, PartialEq, Eq)]

View File

@ -104,9 +104,10 @@ impl<'ast, F: Field + PrimeField, G: Group> fmt::Display for Expression<F, G> {
// Values
Expression::Integer(ref integer) => write!(f, "{}", integer),
Expression::FieldElement(ref fe) => write!(f, "{}", fe),
Expression::GroupElement(ref gr) => write!(f, "{}", gr),
Expression::FieldElement(ref field) => write!(f, "{}", field),
Expression::GroupElement(ref group) => write!(f, "{}", group),
Expression::Boolean(ref bool) => write!(f, "{}", bool.get_value().unwrap()),
Expression::Implicit(ref value) => write!(f, "{}", value),
// Number operations
Expression::Add(ref left, ref right) => write!(f, "{} + {}", left, right),

View File

@ -61,21 +61,17 @@ impl<'ast> types::Integer {
)),
}
}
pub(crate) fn from_implicit(number: String) -> Self {
types::Integer::U128(UInt128::constant(
number.parse::<u128>().expect("unable to parse u128"),
))
}
}
impl<'ast, F: Field + PrimeField, G: Group> From<ast::Integer<'ast>> for types::Expression<F, G> {
fn from(field: ast::Integer<'ast>) -> Self {
types::Expression::Integer(match field._type {
Some(_type) => types::Integer::from(field.number, _type),
// default integer type is u32
None => types::Integer::U32(UInt32::constant(
field
.number
.value
.parse::<u32>()
.expect("unable to parse u32"),
)),
})
types::Expression::Integer(types::Integer::from(field.number, field._type))
}
}
@ -89,6 +85,9 @@ impl<'ast, F: Field + PrimeField, G: Group> From<ast::RangeOrExpression<'ast>>
.from
.map(|from| match types::Expression::<F, G>::from(from.0) {
types::Expression::Integer(number) => number,
types::Expression::Implicit(string) => {
types::Integer::from_implicit(string)
}
expression => {
unimplemented!("Range bounds should be integers, found {}", expression)
}
@ -97,6 +96,9 @@ impl<'ast, F: Field + PrimeField, G: Group> From<ast::RangeOrExpression<'ast>>
.to
.map(|to| match types::Expression::<F, G>::from(to.0) {
types::Expression::Integer(number) => number,
types::Expression::Implicit(string) => {
types::Integer::from_implicit(string)
}
expression => {
unimplemented!("Range bounds should be integers, found {}", expression)
}
@ -124,8 +126,13 @@ impl<'ast, F: Field + PrimeField, G: Group> From<ast::Field<'ast>> for types::Ex
/// pest ast -> types::Group
impl<'ast, F: Field + PrimeField, G: Group> From<ast::Group<'ast>> for types::Expression<F, G> {
fn from(_group: ast::Group<'ast>) -> Self {
types::Expression::GroupElement(G::zero())
fn from(group: ast::Group<'ast>) -> Self {
use std::str::FromStr;
let scalar = G::ScalarField::from_str(&group.number.value).unwrap_or_default();
let point = G::default().mul(&scalar);
types::Expression::GroupElement(point)
}
}
@ -142,6 +149,16 @@ impl<'ast, F: Field + PrimeField, G: Group> From<ast::Boolean<'ast>> for types::
}
}
/// pest ast -> types::NumberImplicit
impl<'ast, F: Field + PrimeField, G: Group> From<ast::NumberImplicit<'ast>>
for types::Expression<F, G>
{
fn from(number: ast::NumberImplicit<'ast>) -> Self {
types::Expression::Implicit(number.number.value)
}
}
/// pest ast -> types::Expression
impl<'ast, F: Field + PrimeField, G: Group> From<ast::Value<'ast>> for types::Expression<F, G> {
@ -151,6 +168,7 @@ impl<'ast, F: Field + PrimeField, G: Group> From<ast::Value<'ast>> for types::Ex
ast::Value::Field(field) => types::Expression::from(field),
ast::Value::Group(group) => types::Expression::from(group),
ast::Value::Boolean(bool) => types::Expression::from(bool),
ast::Value::Implicit(value) => types::Expression::from(value),
}
}
}
@ -369,7 +387,12 @@ impl<'ast, F: Field + PrimeField, G: Group> From<ast::Expression<'ast>>
impl<'ast, F: Field + PrimeField, G: Group> types::Expression<F, G> {
fn get_count(count: ast::Value<'ast>) -> usize {
match count {
ast::Value::Integer(f) => f
ast::Value::Integer(integer) => integer
.number
.value
.parse::<usize>()
.expect("Unable to read array size"),
ast::Value::Implicit(number) => number
.number
.value
.parse::<usize>()
@ -584,10 +607,12 @@ impl<'ast, F: Field + PrimeField, G: Group> From<ast::ForStatement<'ast>>
fn from(statement: ast::ForStatement<'ast>) -> Self {
let from = match types::Expression::<F, G>::from(statement.start) {
types::Expression::Integer(number) => number,
types::Expression::Implicit(string) => types::Integer::from_implicit(string),
expression => unimplemented!("Range bounds should be integers, found {}", expression),
};
let to = match types::Expression::<F, G>::from(statement.stop) {
types::Expression::Integer(number) => number,
types::Expression::Implicit(string) => types::Integer::from_implicit(string),
expression => unimplemented!("Range bounds should be integers, found {}", expression),
};