mirror of
https://github.com/ProvableHQ/leo.git
synced 2024-12-24 02:31:44 +03:00
Merge pull request #11 from AleoHQ/feature/implicit-types
Feature/implicit types
This commit is contained in:
commit
9a1572b14b
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -528,6 +528,7 @@ dependencies = [
|
||||
"snarkos-errors",
|
||||
"snarkos-gadgets",
|
||||
"snarkos-models",
|
||||
"snarkos-utilities",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
|
@ -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
|
||||
}
|
@ -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" }
|
||||
|
@ -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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
),
|
||||
|
@ -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![];
|
||||
|
@ -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))
|
||||
// }
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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()));
|
||||
|
@ -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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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),
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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)]
|
||||
|
@ -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),
|
||||
|
@ -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),
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user