diff --git a/README.md b/README.md index 397dd4fe2c..4e5dca0a8b 100644 --- a/README.md +++ b/README.md @@ -60,19 +60,20 @@ function main() -> field { let c = b - 1; let d = c * 4; let e = d / 2; - let f = e ** 3u32; //Field exponents must be a `u32` type. - return f + return e } ``` -### Group Elements +### Affine Points +The set of affine points on the elliptic curve passed into the leo compiler forms a group. +Leo supports this set as a primitive data type. ```rust function main() -> group { let a = 1000group; // explicit type - let a: group = 21888242871839275222246405745257275088548364400416034343698204186575808495617; // explicit type - let b = a + 1; // implicit type - let c = b - 1; + let a = (21888242871839275222246405745257275088548364400416034343698204186575808495617, 21888242871839275222246405745257275088548364400416034343698204186575808495617)group; // explicit type + let b = a + 0; // implicit type + let c = b - 0; return c } ``` @@ -142,6 +143,8 @@ function main() -> u32 { ``` ### If Else Conditional Statement +** **Experimental** ** +The current constraint system is not optimized for statement branching. Please use the ternary expression above until this feature is stable. ```rust function main(a: private bool, b: private bool) -> u32 { let mut res = 0u32; @@ -158,7 +161,7 @@ function main(a: private bool, b: private bool) -> u32 { ### For loop ```rust -function main() -> field { +function main() -> fe { let mut a = 1field; for i in 0..4 { a = a + 1; @@ -173,7 +176,7 @@ function test1(a : u32) -> u32 { return a + 1 } -function test2(b: field) -> field { +function test2(b: fe) -> field { return b * 2field } @@ -226,6 +229,12 @@ function main(a: public field) -> field { return a } ``` +Private by default. Below `a` is implicitly private. +```rust +function main(a: field) -> field { + return a +} +``` Function inputs are passed by value. ```rust function test(mut a: u32) { @@ -241,28 +250,61 @@ function main() -> u32 { ``` ## Circuits -```rust -bab -``` +Circuits in Leo are similar to classes in object oriented langauges. Circuits are defined above functions in a Leo program. Circuits can have one or more members. + +Members can be defined as fields which hold primitive values ```rust circuit Point { x: u32 y: u32 } function main() -> u32 { - let p = Point {x: 1, y: 0} + let p = Point {x: 1, y: 0}; return p.x } ``` + +Members can also be defined as functions. ```rust -circuit Foo { - x: bool +circuit Circ { + function echo(x: u32) -> u32 { + return x + } } -function main() -> Foo { - let mut f = Foo {x: true}; - f.x = false; - return f + +function main() -> u32 { + let c = Circ { }; + return c.echo(1u32) +} +``` + +Circuit functions can be made static, enabling them to be called without instantiation. +```rust +circuit Circ { + static function echo(x: u32) -> u32 { + return x + } +} + +function main() -> u32 { + return Circ::echo(1u32) +} +``` + +The `Self` keyword is supported in circuit functions. +```rust +circuit Circ { + b: bool + + static function new() -> Self { + return Self { b: true } + } +} + +function main() -> Circ { + let c = Circ::new(); + return c.b } ``` @@ -311,12 +353,34 @@ This will enforce that the two values are equal in the constraint system. function main() { assert_eq(45, 45); - assert_eq(2field, 2field); + assert_eq(2fe, 2fe); assert_eq(true, true); } ``` +## Testing + +Use the `test` keyword to add tests to a leo program. Tests must have 0 function inputs and 0 function returns. + +```rust +function main(a: u32) -> u32 { + return a +} + +test function expect_pass() { + let a = 1u32; + + let res = main(a); + + assert_eq!(res, 1u32); +} + +test function expect_fail() { + assert_eq!(1u8, 0u8); +} +``` + # Leo CLI @@ -361,6 +425,12 @@ To execute unit tests on your program, run: ``` leo test ``` +The results of test compilation and the constraint system will be printed: +``` + INFO leo Running 2 tests + INFO leo test language::expect_pass compiled. Constraint system satisfied: true +ERROR leo test language::expect_fail errored: Assertion 1u8 == 0u8 failed +``` ## Run diff --git a/compiler/src/ast.rs b/compiler/src/ast.rs index bbfe3da3d0..f4c16d7525 100644 --- a/compiler/src/ast.rs +++ b/compiler/src/ast.rs @@ -90,10 +90,10 @@ pub enum BinaryOperator { Or, And, Eq, - Neq, - Geq, + Ne, + Ge, Gt, - Leq, + Le, Lt, Add, Sub, @@ -812,10 +812,10 @@ fn precedence_climber() -> PrecClimber { Operator::new(Rule::operation_or, Assoc::Left), Operator::new(Rule::operation_and, Assoc::Left), Operator::new(Rule::operation_eq, Assoc::Left) - | Operator::new(Rule::operation_neq, Assoc::Left), - Operator::new(Rule::operation_geq, Assoc::Left) + | Operator::new(Rule::operation_ne, Assoc::Left), + Operator::new(Rule::operation_ge, Assoc::Left) | Operator::new(Rule::operation_gt, Assoc::Left) - | Operator::new(Rule::operation_leq, Assoc::Left) + | Operator::new(Rule::operation_le, Assoc::Left) | Operator::new(Rule::operation_lt, Assoc::Left), Operator::new(Rule::operation_add, Assoc::Left) | Operator::new(Rule::operation_sub, Assoc::Left), @@ -927,10 +927,10 @@ fn binary_expression<'ast>( Rule::operation_or => Expression::binary(BinaryOperator::Or, lhs, rhs, span), Rule::operation_and => Expression::binary(BinaryOperator::And, lhs, rhs, span), Rule::operation_eq => Expression::binary(BinaryOperator::Eq, lhs, rhs, span), - Rule::operation_neq => Expression::binary(BinaryOperator::Neq, lhs, rhs, span), - Rule::operation_geq => Expression::binary(BinaryOperator::Geq, lhs, rhs, span), + Rule::operation_ne => Expression::binary(BinaryOperator::Ne, lhs, rhs, span), + Rule::operation_ge => Expression::binary(BinaryOperator::Ge, lhs, rhs, span), Rule::operation_gt => Expression::binary(BinaryOperator::Gt, lhs, rhs, span), - Rule::operation_leq => Expression::binary(BinaryOperator::Leq, lhs, rhs, span), + Rule::operation_le => Expression::binary(BinaryOperator::Le, lhs, rhs, span), Rule::operation_lt => Expression::binary(BinaryOperator::Lt, lhs, rhs, span), Rule::operation_add => Expression::binary(BinaryOperator::Add, lhs, rhs, span), Rule::operation_sub => Expression::binary(BinaryOperator::Sub, lhs, rhs, span), diff --git a/compiler/src/compiler.rs b/compiler/src/compiler.rs index e2a2eba57d..0e0914e411 100644 --- a/compiler/src/compiler.rs +++ b/compiler/src/compiler.rs @@ -21,8 +21,8 @@ use std::{fs, marker::PhantomData, path::PathBuf}; pub struct Compiler> { package_name: String, main_file_path: PathBuf, - program: Program, - program_inputs: Vec>>, + program: Program, + program_inputs: Vec>, output: Option>, _engine: PhantomData, } @@ -44,7 +44,7 @@ impl> Compiler { Ok(program) } - pub fn set_inputs(&mut self, program_inputs: Vec>>) { + pub fn set_inputs(&mut self, program_inputs: Vec>) { self.program_inputs = program_inputs; } @@ -98,7 +98,7 @@ impl> Compiler { // Build program from abstract syntax tree let package_name = self.package_name.clone(); - self.program = Program::::from(syntax_tree, package_name); + self.program = Program::from(syntax_tree, package_name); self.program_inputs = vec![None; self.program.num_parameters]; log::debug!("Program parsing complete\n{:#?}", self.program); diff --git a/compiler/src/constraints/boolean.rs b/compiler/src/constraints/boolean.rs index 12999ad42e..24f885dada 100644 --- a/compiler/src/constraints/boolean.rs +++ b/compiler/src/constraints/boolean.rs @@ -22,7 +22,7 @@ impl, CS: ConstraintSystem> Constraine cs: &mut CS, name: String, private: bool, - input_value: Option>, + input_value: Option, ) -> Result, BooleanError> { // Check that the input value is the correct type let bool_value = match input_value { diff --git a/compiler/src/constraints/expression.rs b/compiler/src/constraints/expression.rs index 642ebc4c57..3a60732bed 100644 --- a/compiler/src/constraints/expression.rs +++ b/compiler/src/constraints/expression.rs @@ -8,7 +8,7 @@ use crate::{ CircuitFieldDefinition, CircuitMember, Expression, Identifier, RangeOrExpression, SpreadOrExpression, }, - GroupType, Integer, IntegerType, Type, + FieldType, GroupType, Integer, IntegerType, Type, }; use snarkos_models::{ @@ -25,8 +25,8 @@ impl, CS: ConstraintSystem> Constraine &mut self, file_scope: String, function_scope: String, - expected_types: &Vec>, - unresolved_identifier: Identifier, + expected_types: &Vec, + unresolved_identifier: Identifier, ) -> Result, ExpressionError> { // Evaluate the identifier name in the current function scope let variable_name = new_scope(function_scope, unresolved_identifier.to_string()); @@ -60,8 +60,8 @@ impl, CS: ConstraintSystem> Constraine (ConstrainedValue::Integer(num_1), ConstrainedValue::Integer(num_2)) => { Ok(Self::enforce_integer_add(cs, num_1, num_2)?) } - (ConstrainedValue::FieldElement(fe_1), ConstrainedValue::FieldElement(fe_2)) => { - Ok(self.enforce_field_add(cs, fe_1, fe_2)?) + (ConstrainedValue::Field(fe_1), ConstrainedValue::Field(fe_2)) => { + Ok(ConstrainedValue::Field(fe_1.add(cs, &fe_2)?)) } (ConstrainedValue::Group(ge_1), ConstrainedValue::Group(ge_2)) => { Ok(ConstrainedValue::Group(ge_1.add(cs, &ge_2)?)) @@ -91,8 +91,8 @@ impl, CS: ConstraintSystem> Constraine (ConstrainedValue::Integer(num_1), ConstrainedValue::Integer(num_2)) => { Ok(Self::enforce_integer_sub(cs, num_1, num_2)?) } - (ConstrainedValue::FieldElement(fe_1), ConstrainedValue::FieldElement(fe_2)) => { - Ok(self.enforce_field_sub(cs, fe_1, fe_2)?) + (ConstrainedValue::Field(fe_1), ConstrainedValue::Field(fe_2)) => { + Ok(ConstrainedValue::Field(fe_1.sub(cs, &fe_2)?)) } (ConstrainedValue::Group(ge_1), ConstrainedValue::Group(ge_2)) => { Ok(ConstrainedValue::Group(ge_1.sub(cs, &ge_2)?)) @@ -122,8 +122,8 @@ impl, CS: ConstraintSystem> Constraine (ConstrainedValue::Integer(num_1), ConstrainedValue::Integer(num_2)) => { Ok(Self::enforce_integer_mul(cs, num_1, num_2)?) } - (ConstrainedValue::FieldElement(fe_1), ConstrainedValue::FieldElement(fe_2)) => { - Ok(self.enforce_field_mul(cs, fe_1, fe_2)?) + (ConstrainedValue::Field(fe_1), ConstrainedValue::Field(fe_2)) => { + Ok(ConstrainedValue::Field(fe_1.mul(cs, &fe_2)?)) } (ConstrainedValue::Unresolved(string), val_2) => { let val_1 = ConstrainedValue::from_other(string, &val_2)?; @@ -152,8 +152,8 @@ impl, CS: ConstraintSystem> Constraine (ConstrainedValue::Integer(num_1), ConstrainedValue::Integer(num_2)) => { Ok(Self::enforce_integer_div(cs, num_1, num_2)?) } - (ConstrainedValue::FieldElement(fe_1), ConstrainedValue::FieldElement(fe_2)) => { - Ok(self.enforce_field_div(cs, fe_1, fe_2)?) + (ConstrainedValue::Field(fe_1), ConstrainedValue::Field(fe_2)) => { + Ok(ConstrainedValue::Field(fe_1.div(cs, &fe_2)?)) } (ConstrainedValue::Unresolved(string), val_2) => { let val_1 = ConstrainedValue::from_other(string, &val_2)?; @@ -181,9 +181,6 @@ impl, CS: ConstraintSystem> Constraine (ConstrainedValue::Integer(num_1), ConstrainedValue::Integer(num_2)) => { Ok(Self::enforce_integer_pow(cs, num_1, num_2)?) } - (ConstrainedValue::FieldElement(fe_1), ConstrainedValue::Integer(num_2)) => { - Ok(self.enforce_field_pow(cs, fe_1, num_2)?) - } (ConstrainedValue::Unresolved(string), val_2) => { let val_1 = ConstrainedValue::from_other(string, &val_2)?; self.enforce_pow_expression(cs, val_1, val_2) @@ -192,9 +189,6 @@ impl, CS: ConstraintSystem> Constraine let val_2 = ConstrainedValue::from_other(string, &val_1)?; self.enforce_pow_expression(cs, val_1, val_2) } - (_, ConstrainedValue::FieldElement(num_2)) => { - Err(ExpressionError::InvalidExponent(num_2.to_string())) - } (val_1, val_2) => Err(ExpressionError::IncompatibleTypes(format!( "{} * {}", val_1, val_2, @@ -215,9 +209,9 @@ impl, CS: ConstraintSystem> Constraine (ConstrainedValue::Integer(num_1), ConstrainedValue::Integer(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::Field(fe_1), ConstrainedValue::Field(fe_2)) => { + Ok(ConstrainedValue::Boolean(Boolean::Constant(fe_1.eq(&fe_2)))) + } (ConstrainedValue::Group(ge_1), ConstrainedValue::Group(ge_2)) => { Ok(ConstrainedValue::Boolean(Boolean::Constant(ge_1.eq(&ge_2)))) } @@ -236,22 +230,23 @@ impl, CS: ConstraintSystem> Constraine } } - fn evaluate_geq_expression( + fn evaluate_ge_expression( &mut self, left: ConstrainedValue, right: ConstrainedValue, ) -> Result, ExpressionError> { match (left, right) { - // (ResolvedValue::FieldElement(fe_1), ResolvedValue::FieldElement(fe_2)) => { - // Self::field_geq(fe_1, fe_2) - // } + (ConstrainedValue::Field(fe_1), ConstrainedValue::Field(fe_2)) => { + let result = fe_1.ge(&fe_2); + Ok(ConstrainedValue::Boolean(Boolean::Constant(result))) + } (ConstrainedValue::Unresolved(string), val_2) => { let val_1 = ConstrainedValue::from_other(string, &val_2)?; - self.evaluate_geq_expression(val_1, val_2) + self.evaluate_ge_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) + self.evaluate_ge_expression(val_1, val_2) } (val_1, val_2) => Err(ExpressionError::IncompatibleTypes(format!( "{} >= {}, values must be fields", @@ -266,9 +261,10 @@ impl, CS: ConstraintSystem> Constraine right: ConstrainedValue, ) -> Result, ExpressionError> { match (left, right) { - // (ResolvedValue::FieldElement(fe_1), ResolvedValue::FieldElement(fe_2)) => { - // Self::field_gt(fe_1, fe_2) - // } + (ConstrainedValue::Field(fe_1), ConstrainedValue::Field(fe_2)) => { + let result = fe_1.gt(&fe_2); + Ok(ConstrainedValue::Boolean(Boolean::Constant(result))) + } (ConstrainedValue::Unresolved(string), val_2) => { let val_1 = ConstrainedValue::from_other(string, &val_2)?; self.evaluate_gt_expression(val_1, val_2) @@ -284,22 +280,23 @@ impl, CS: ConstraintSystem> Constraine } } - fn evaluate_leq_expression( + fn evaluate_le_expression( &mut self, left: ConstrainedValue, right: ConstrainedValue, ) -> Result, ExpressionError> { match (left, right) { - // (ResolvedValue::FieldElement(fe_1), ResolvedValue::FieldElement(fe_2)) => { - // Self::field_leq(fe_1, fe_2) - // } + (ConstrainedValue::Field(fe_1), ConstrainedValue::Field(fe_2)) => { + let result = fe_1.le(&fe_2); + Ok(ConstrainedValue::Boolean(Boolean::Constant(result))) + } (ConstrainedValue::Unresolved(string), val_2) => { let val_1 = ConstrainedValue::from_other(string, &val_2)?; - self.evaluate_leq_expression(val_1, val_2) + self.evaluate_le_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) + self.evaluate_le_expression(val_1, val_2) } (val_1, val_2) => Err(ExpressionError::IncompatibleTypes(format!( "{} <= {}, values must be fields", @@ -314,9 +311,10 @@ impl, CS: ConstraintSystem> Constraine right: ConstrainedValue, ) -> Result, ExpressionError> { match (left, right) { - // (ResolvedValue::FieldElement(fe_1), ResolvedValue::FieldElement(fe_2)) => { - // Self::field_lt(fe_1, fe_2) - // } + (ConstrainedValue::Field(fe_1), ConstrainedValue::Field(fe_2)) => { + let result = fe_1.lt(&fe_2); + Ok(ConstrainedValue::Boolean(Boolean::Constant(result))) + } (ConstrainedValue::Unresolved(string), val_2) => { let val_1 = ConstrainedValue::from_other(string, &val_2)?; self.evaluate_lt_expression(val_1, val_2) @@ -338,10 +336,10 @@ impl, CS: ConstraintSystem> Constraine cs: &mut CS, file_scope: String, function_scope: String, - expected_types: &Vec>, - first: Expression, - second: Expression, - third: Expression, + expected_types: &Vec, + first: Expression, + second: Expression, + third: Expression, ) -> Result, ExpressionError> { let resolved_first = match self.enforce_expression( cs, @@ -374,6 +372,10 @@ impl, CS: ConstraintSystem> Constraine Integer::conditionally_select(cs, &resolved_first, &integer_2, &integer_3)?; Ok(ConstrainedValue::Integer(result)) } + (ConstrainedValue::Field(fe_1), ConstrainedValue::Field(fe_2)) => { + let result = FieldType::conditionally_select(cs, &resolved_first, &fe_1, &fe_2)?; + Ok(ConstrainedValue::Field(result)) + } (ConstrainedValue::Group(ge_1), ConstrainedValue::Group(ge_2)) => { let result = G::conditionally_select(cs, &resolved_first, &ge_1, &ge_2)?; Ok(ConstrainedValue::Group(result)) @@ -390,8 +392,8 @@ impl, CS: ConstraintSystem> Constraine cs: &mut CS, file_scope: String, function_scope: String, - expected_types: &Vec>, - array: Vec>>, + expected_types: &Vec, + array: Vec>, ) -> Result, ExpressionError> { // Check explicit array type dimension if given let mut expected_types = expected_types.clone(); @@ -454,7 +456,7 @@ impl, CS: ConstraintSystem> Constraine cs: &mut CS, file_scope: String, function_scope: String, - index: Expression, + index: Expression, ) -> Result { let expected_types = vec![Type::IntegerType(IntegerType::U32)]; match self.enforce_branch( @@ -474,9 +476,9 @@ impl, CS: ConstraintSystem> Constraine cs: &mut CS, file_scope: String, function_scope: String, - expected_types: &Vec>, - array: Box>, - index: RangeOrExpression, + expected_types: &Vec, + array: Box, + index: RangeOrExpression, ) -> Result, ExpressionError> { let array = match self.enforce_branch( cs, @@ -515,8 +517,8 @@ impl, CS: ConstraintSystem> Constraine cs: &mut CS, file_scope: String, function_scope: String, - identifier: Identifier, - members: Vec>, + identifier: Identifier, + members: Vec, ) -> Result, ExpressionError> { let mut program_identifier = new_scope(file_scope.clone(), identifier.to_string()); @@ -589,9 +591,9 @@ impl, CS: ConstraintSystem> Constraine cs: &mut CS, file_scope: String, function_scope: String, - expected_types: &Vec>, - circuit_identifier: Box>, - circuit_member: Identifier, + expected_types: &Vec, + circuit_identifier: Box, + circuit_member: Identifier, ) -> Result, ExpressionError> { let (circuit_name, members) = match self.enforce_branch( cs, @@ -650,9 +652,9 @@ impl, CS: ConstraintSystem> Constraine cs: &mut CS, file_scope: String, function_scope: String, - expected_types: &Vec>, - circuit_identifier: Box>, - circuit_member: Identifier, + expected_types: &Vec, + circuit_identifier: Box, + circuit_member: Identifier, ) -> Result, ExpressionError> { // Get defined circuit let circuit = match self.enforce_expression( @@ -704,9 +706,9 @@ impl, CS: ConstraintSystem> Constraine cs: &mut CS, file_scope: String, function_scope: String, - expected_types: &Vec>, - function: Box>, - arguments: Vec>, + expected_types: &Vec, + function: Box, + arguments: Vec, ) -> Result, ExpressionError> { let function_value = self.enforce_expression( cs, @@ -743,7 +745,7 @@ impl, CS: ConstraintSystem> Constraine } pub(crate) fn enforce_number_implicit( - expected_types: &Vec>, + expected_types: &Vec, value: String, ) -> Result, ExpressionError> { if expected_types.len() == 1 { @@ -761,8 +763,8 @@ impl, CS: ConstraintSystem> Constraine cs: &mut CS, file_scope: String, function_scope: String, - expected_types: &Vec>, - expression: Expression, + expected_types: &Vec, + expression: Expression, ) -> Result, ExpressionError> { let mut branch = self.enforce_expression(cs, file_scope, function_scope, expected_types, expression)?; @@ -778,9 +780,9 @@ impl, CS: ConstraintSystem> Constraine cs: &mut CS, file_scope: String, function_scope: String, - expected_types: &Vec>, - left: Expression, - right: Expression, + expected_types: &Vec, + left: Expression, + right: Expression, ) -> Result<(ConstrainedValue, ConstrainedValue), ExpressionError> { let resolved_left = self.enforce_branch( cs, @@ -805,8 +807,8 @@ impl, CS: ConstraintSystem> Constraine cs: &mut CS, file_scope: String, function_scope: String, - expected_types: &Vec>, - expression: Expression, + expected_types: &Vec, + expression: Expression, ) -> Result, ExpressionError> { match expression { // Variables @@ -819,7 +821,7 @@ impl, CS: ConstraintSystem> Constraine // Values Expression::Integer(integer) => Ok(Self::get_integer_constant(integer)), - Expression::FieldElement(fe) => Ok(Self::get_field_element_constant(fe)), + Expression::Field(field) => Ok(ConstrainedValue::Field(FieldType::constant(field)?)), Expression::Group(group_affine) => { Ok(ConstrainedValue::Group(G::constant(group_affine)?)) } @@ -932,7 +934,7 @@ impl, CS: ConstraintSystem> Constraine Ok(self.evaluate_eq_expression(resolved_left, resolved_right)?) } - Expression::Geq(left, right) => { + Expression::Ge(left, right) => { let (resolved_left, resolved_right) = self.enforce_binary_expression( cs, file_scope.clone(), @@ -942,7 +944,7 @@ impl, CS: ConstraintSystem> Constraine *right, )?; - Ok(self.evaluate_geq_expression(resolved_left, resolved_right)?) + Ok(self.evaluate_ge_expression(resolved_left, resolved_right)?) } Expression::Gt(left, right) => { let (resolved_left, resolved_right) = self.enforce_binary_expression( @@ -956,7 +958,7 @@ impl, CS: ConstraintSystem> Constraine Ok(self.evaluate_gt_expression(resolved_left, resolved_right)?) } - Expression::Leq(left, right) => { + Expression::Le(left, right) => { let (resolved_left, resolved_right) = self.enforce_binary_expression( cs, file_scope.clone(), @@ -966,7 +968,7 @@ impl, CS: ConstraintSystem> Constraine *right, )?; - Ok(self.evaluate_leq_expression(resolved_left, resolved_right)?) + Ok(self.evaluate_le_expression(resolved_left, resolved_right)?) } Expression::Lt(left, right) => { let (resolved_left, resolved_right) = self.enforce_binary_expression( diff --git a/compiler/src/constraints/field.rs b/compiler/src/constraints/field.rs new file mode 100644 index 0000000000..ea2a682b11 --- /dev/null +++ b/compiler/src/constraints/field.rs @@ -0,0 +1,43 @@ +//! Methods to enforce constraints on field elements in a resolved Leo program. + +use crate::{ + constraints::ConstrainedValue, errors::FieldError, types::InputValue, FieldType, GroupType, +}; + +use snarkos_errors::gadgets::SynthesisError; +use snarkos_models::{ + curves::{Field, PrimeField}, + gadgets::{r1cs::ConstraintSystem, utilities::alloc::AllocGadget}, +}; + +pub(crate) fn field_from_input, CS: ConstraintSystem>( + cs: &mut CS, + name: String, + private: bool, + input_value: Option, +) -> Result, FieldError> { + // Check that the parameter value is the correct type + let field_option = match input_value { + Some(input) => { + if let InputValue::Field(field_string) = input { + Some(field_string) + } else { + return Err(FieldError::Invalid(input.to_string())); + } + } + None => None, + }; + + // Check visibility of parameter + let field_value = if private { + FieldType::alloc(cs.ns(|| name), || { + field_option.ok_or(SynthesisError::AssignmentMissing) + })? + } else { + FieldType::alloc_input(cs.ns(|| name), || { + field_option.ok_or(SynthesisError::AssignmentMissing) + })? + }; + + Ok(ConstrainedValue::Field(field_value)) +} diff --git a/compiler/src/constraints/field_element.rs b/compiler/src/constraints/field_element.rs deleted file mode 100644 index ff690c287b..0000000000 --- a/compiler/src/constraints/field_element.rs +++ /dev/null @@ -1,471 +0,0 @@ -//! Methods to enforce constraints on field elements in a resolved Leo program. - -use crate::{ - constraints::{ConstrainedProgram, ConstrainedValue}, - errors::FieldElementError, - types::{FieldElement, InputValue, Integer}, - GroupType, -}; - -use snarkos_errors::gadgets::SynthesisError; -use snarkos_models::{ - curves::{Field, PrimeField}, - gadgets::r1cs::{ConstraintSystem, LinearCombination, Variable as R1CSVariable}, -}; - -impl, CS: ConstraintSystem> ConstrainedProgram { - pub(crate) fn field_element_from_input( - &mut self, - cs: &mut CS, - name: String, - private: bool, - input_value: Option>, - ) -> Result, FieldElementError> { - // Check that the parameter value is the correct type - let field_option = match input_value { - Some(input) => { - if let InputValue::Field(fe) = input { - Some(fe) - } else { - return Err(FieldElementError::InvalidField(input.to_string())); - } - } - None => None, - }; - - // Check visibility of parameter - let field_value = if private { - cs.alloc( - || name, - || field_option.ok_or(SynthesisError::AssignmentMissing), - )? - } else { - cs.alloc_input( - || name, - || field_option.ok_or(SynthesisError::AssignmentMissing), - )? - }; - - Ok(ConstrainedValue::FieldElement(FieldElement::Allocated( - field_option, - field_value, - ))) - } - - pub(crate) fn get_field_element_constant(fe: FieldElement) -> ConstrainedValue { - ConstrainedValue::FieldElement(fe) - } - - // pub(crate) fn field_eq(fe1: F, fe2: F) -> ResolvedValue { - // ResolvedValue::Boolean(Boolean::Constant(fe1.eq(&fe2))) - // } - // - // pub(crate) fn field_geq(fe1: F, fe2: F) -> ResolvedValue { - // ResolvedValue::Boolean(Boolean::Constant(fe1.ge(&fe2))) - // } - // - // pub(crate) fn field_gt(fe1: F, fe2: F) -> ResolvedValue { - // ResolvedValue::Boolean(Boolean::Constant(fe1.gt(&fe2))) - // } - // - // pub(crate) fn field_leq(fe1: F, fe2: F) -> ResolvedValue { - // ResolvedValue::Boolean(Boolean::Constant(fe1.le(&fe2))) - // } - // - // pub(crate) fn field_lt(fe1: F, fe2: F) -> ResolvedValue { - // ResolvedValue::Boolean(Boolean::Constant(fe1.lt(&fe2))) - // } - - pub(crate) fn enforce_field_eq( - &mut self, - cs: &mut CS, - fe_1: FieldElement, - fe_2: FieldElement, - ) { - let mut lc = LinearCombination::zero(); - - match (fe_1, fe_2) { - (FieldElement::Constant(fe_1_constant), FieldElement::Constant(fe_2_constant)) => { - // lc = lc + (fe_1_constant * 1) - (fe_2_constant * 1) - // lc = lc + fe_1 - fe_2 - lc = lc + (fe_1_constant, CS::one()) - (fe_2_constant, CS::one()); - } - // else, return an allocated result - ( - FieldElement::Allocated(_fe_1_value, fe_1_variable), - FieldElement::Constant(fe_2_constant), - ) => { - // lc = lc + fe_1 - (fe_2_constant * 1) - // lc = lc + fe_1 - fe_2 - lc = lc + fe_1_variable - (fe_2_constant, CS::one()) - } - ( - FieldElement::Constant(fe_1_constant), - FieldElement::Allocated(_fe_2_value, fe_2_variable), - ) => { - // lc = lc + (fe_1_constant * 1) - fe_2 - // lc = lc + fe_1 - fe_2 - lc = lc + (fe_1_constant, CS::one()) - fe_2_variable - } - ( - FieldElement::Allocated(_fe_1_value, fe_1_variable), - FieldElement::Allocated(_fe_2_value, fe_2_variable), - ) => { - // lc = lc + fe_1 - fe_2 - lc = lc + fe_1_variable - fe_2_variable - } - } - - // enforce that the linear combination is zero - cs.enforce(|| "field equality", |lc| lc, |lc| lc, |_| lc); - } - - pub(crate) fn enforce_field_add( - &mut self, - cs: &mut CS, - fe_1: FieldElement, - fe_2: FieldElement, - ) -> Result, FieldElementError> { - Ok(match (fe_1, fe_2) { - // if both constants, then return a constant result - (FieldElement::Constant(fe_1_constant), FieldElement::Constant(fe_2_constant)) => { - ConstrainedValue::FieldElement(FieldElement::Constant( - fe_1_constant.add(&fe_2_constant), - )) - } - // else, return an allocated result - ( - FieldElement::Allocated(fe_1_value, fe_1_variable), - FieldElement::Constant(fe_2_constant), - ) => { - let sum_value: Option = fe_1_value.map(|v| v.add(&fe_2_constant)); - let sum_variable: R1CSVariable = cs.alloc( - || "field addition", - || sum_value.ok_or(SynthesisError::AssignmentMissing), - )?; - - cs.enforce( - || "sum = 1 * (fe_1 + fe2)", - |lc| lc + CS::one(), - |lc| lc + fe_1_variable + (fe_2_constant, CS::one()), - |lc| lc + sum_variable.clone(), - ); - - ConstrainedValue::FieldElement(FieldElement::Allocated(sum_value, sum_variable)) - } - ( - FieldElement::Constant(fe_1_constant), - FieldElement::Allocated(fe_2_value, fe_2_variable), - ) => { - let sum_value: Option = fe_2_value.map(|v| fe_1_constant.add(&v)); - let sum_variable: R1CSVariable = cs.alloc( - || "field addition", - || sum_value.ok_or(SynthesisError::AssignmentMissing), - )?; - - cs.enforce( - || "sum = 1 * (fe_1 + fe_2)", - |lc| lc + CS::one(), - |lc| lc + (fe_1_constant, CS::one()) + fe_2_variable, - |lc| lc + sum_variable.clone(), - ); - - ConstrainedValue::FieldElement(FieldElement::Allocated(sum_value, sum_variable)) - } - ( - FieldElement::Allocated(fe_1_value, fe_1_variable), - FieldElement::Allocated(fe_2_value, fe_2_variable), - ) => { - let sum_value: Option = match (fe_1_value, fe_2_value) { - (Some(fe_1_value), Some(fe_2_value)) => Some(fe_1_value.add(&fe_2_value)), - (_, _) => None, - }; - let sum_variable: R1CSVariable = cs.alloc( - || "field addition", - || sum_value.ok_or(SynthesisError::AssignmentMissing), - )?; - - cs.enforce( - || "sum = 1 * (fe_1 + fe_2)", - |lc| lc + CS::one(), - |lc| lc + fe_1_variable + fe_2_variable, - |lc| lc + sum_variable.clone(), - ); - - ConstrainedValue::FieldElement(FieldElement::Allocated(sum_value, sum_variable)) - } - }) - } - - pub(crate) fn enforce_field_sub( - &mut self, - cs: &mut CS, - fe_1: FieldElement, - fe_2: FieldElement, - ) -> Result, FieldElementError> { - Ok(match (fe_1, fe_2) { - // if both constants, then return a constant result - (FieldElement::Constant(fe_1_constant), FieldElement::Constant(fe_2_constant)) => { - ConstrainedValue::FieldElement(FieldElement::Constant( - fe_1_constant.sub(&fe_2_constant), - )) - } - // else, return an allocated result - ( - FieldElement::Allocated(fe_1_value, fe_1_variable), - FieldElement::Constant(fe_2_constant), - ) => { - let sub_value: Option = fe_1_value.map(|v| v.sub(&fe_2_constant)); - let sub_variable: R1CSVariable = cs.alloc( - || "field subtraction", - || sub_value.ok_or(SynthesisError::AssignmentMissing), - )?; - - cs.enforce( - || "sub = 1 * (fe_1 - fe2)", - |lc| lc + CS::one(), - |lc| lc + fe_1_variable - (fe_2_constant, CS::one()), - |lc| lc + sub_variable.clone(), - ); - - ConstrainedValue::FieldElement(FieldElement::Allocated(sub_value, sub_variable)) - } - ( - FieldElement::Constant(fe_1_constant), - FieldElement::Allocated(fe_2_value, fe_2_variable), - ) => { - let sub_value: Option = fe_2_value.map(|v| fe_1_constant.sub(&v)); - let sub_variable: R1CSVariable = cs.alloc( - || "field subtraction", - || sub_value.ok_or(SynthesisError::AssignmentMissing), - )?; - - cs.enforce( - || "sub = 1 * (fe_1 - fe_2)", - |lc| lc + CS::one(), - |lc| lc + (fe_1_constant, CS::one()) - fe_2_variable, - |lc| lc + sub_variable.clone(), - ); - - ConstrainedValue::FieldElement(FieldElement::Allocated(sub_value, sub_variable)) - } - ( - FieldElement::Allocated(fe_1_value, fe_1_variable), - FieldElement::Allocated(fe_2_value, fe_2_variable), - ) => { - let sub_value: Option = match (fe_1_value, fe_2_value) { - (Some(fe_1_value), Some(fe_2_value)) => Some(fe_1_value.sub(&fe_2_value)), - (_, _) => None, - }; - let sub_variable: R1CSVariable = cs.alloc( - || "field subtraction", - || sub_value.ok_or(SynthesisError::AssignmentMissing), - )?; - - cs.enforce( - || "sub = 1 * (fe_1 - fe_2)", - |lc| lc + CS::one(), - |lc| lc + fe_1_variable - fe_2_variable, - |lc| lc + sub_variable.clone(), - ); - - ConstrainedValue::FieldElement(FieldElement::Allocated(sub_value, sub_variable)) - } - }) - } - - pub(crate) fn enforce_field_mul( - &mut self, - cs: &mut CS, - fe_1: FieldElement, - fe_2: FieldElement, - ) -> Result, FieldElementError> { - Ok(match (fe_1, fe_2) { - // if both constants, then return a constant result - (FieldElement::Constant(fe_1_constant), FieldElement::Constant(fe_2_constant)) => { - ConstrainedValue::FieldElement(FieldElement::Constant( - fe_1_constant.mul(&fe_2_constant), - )) - } - // else, return an allocated result - ( - FieldElement::Allocated(fe_1_value, fe_1_variable), - FieldElement::Constant(fe_2_constant), - ) => { - let mul_value: Option = fe_1_value.map(|v| v.mul(&fe_2_constant)); - let mul_variable: R1CSVariable = cs.alloc( - || "field multiplication", - || mul_value.ok_or(SynthesisError::AssignmentMissing), - )?; - - cs.enforce( - || "mul = fe_1 * fe_2", - |lc| lc + fe_1_variable, - |lc| lc + (fe_2_constant, CS::one()), - |lc| lc + mul_variable.clone(), - ); - - ConstrainedValue::FieldElement(FieldElement::Allocated(mul_value, mul_variable)) - } - ( - FieldElement::Constant(fe_1_constant), - FieldElement::Allocated(fe_2_value, fe_2_variable), - ) => { - let mul_value: Option = fe_2_value.map(|v| fe_1_constant.mul(&v)); - let mul_variable: R1CSVariable = cs.alloc( - || "field multiplication", - || mul_value.ok_or(SynthesisError::AssignmentMissing), - )?; - - cs.enforce( - || "mul = fe_1 * fe_2", - |lc| lc + (fe_1_constant, CS::one()), - |lc| lc + fe_2_variable, - |lc| lc + mul_variable.clone(), - ); - - ConstrainedValue::FieldElement(FieldElement::Allocated(mul_value, mul_variable)) - } - ( - FieldElement::Allocated(fe_1_value, fe_1_variable), - FieldElement::Allocated(fe_2_value, fe_2_variable), - ) => { - let mul_value: Option = match (fe_1_value, fe_2_value) { - (Some(fe_1_value), Some(fe_2_value)) => Some(fe_1_value.mul(&fe_2_value)), - (_, _) => None, - }; - let mul_variable: R1CSVariable = cs.alloc( - || "field multiplication", - || mul_value.ok_or(SynthesisError::AssignmentMissing), - )?; - - cs.enforce( - || "mul = fe_1 * fe_2", - |lc| lc + fe_1_variable, - |lc| lc + fe_2_variable, - |lc| lc + mul_variable.clone(), - ); - - ConstrainedValue::FieldElement(FieldElement::Allocated(mul_value, mul_variable)) - } - }) - } - - pub(crate) fn enforce_field_div( - &mut self, - cs: &mut CS, - fe_1: FieldElement, - fe_2: FieldElement, - ) -> Result, FieldElementError> { - Ok(match (fe_1, fe_2) { - // if both constants, then return a constant result - (FieldElement::Constant(fe_1_constant), FieldElement::Constant(fe_2_constant)) => { - ConstrainedValue::FieldElement(FieldElement::Constant( - fe_1_constant.div(&fe_2_constant), - )) - } - // else, return an allocated result - ( - FieldElement::Allocated(fe_1_value, fe_1_variable), - FieldElement::Constant(fe_2_constant), - ) => { - let div_value: Option = fe_1_value.map(|v| v.div(&fe_2_constant)); - let div_variable: R1CSVariable = cs.alloc( - || "field division", - || div_value.ok_or(SynthesisError::AssignmentMissing), - )?; - let fe_2_inverse_value = fe_2_constant.inverse().unwrap(); - - cs.enforce( - || "div = fe_1 * fe_2^-1", - |lc| lc + fe_1_variable, - |lc| lc + (fe_2_inverse_value, CS::one()), - |lc| lc + div_variable.clone(), - ); - - ConstrainedValue::FieldElement(FieldElement::Allocated(div_value, div_variable)) - } - ( - FieldElement::Constant(fe_1_constant), - FieldElement::Allocated(fe_2_value, _fe_2_variable), - ) => { - let div_value: Option = fe_2_value.map(|v| fe_1_constant.div(&v)); - let div_variable: R1CSVariable = cs.alloc( - || "field division", - || div_value.ok_or(SynthesisError::AssignmentMissing), - )?; - let fe_2_inverse_value = fe_2_value.map(|v| v.inverse().unwrap()); - let fe_2_inverse_variable = cs.alloc( - || "field inverse", - || fe_2_inverse_value.ok_or(SynthesisError::AssignmentMissing), - )?; - - cs.enforce( - || "div = fe_1 * fe_2^-1", - |lc| lc + (fe_1_constant, CS::one()), - |lc| lc + fe_2_inverse_variable, - |lc| lc + div_variable.clone(), - ); - - ConstrainedValue::FieldElement(FieldElement::Allocated(div_value, div_variable)) - } - ( - FieldElement::Allocated(fe_1_value, fe_1_variable), - FieldElement::Allocated(fe_2_value, _fe_2_variable), - ) => { - let div_value: Option = match (fe_1_value, fe_2_value) { - (Some(fe_1_value), Some(fe_2_value)) => Some(fe_1_value.div(&fe_2_value)), - (_, _) => None, - }; - let div_variable: R1CSVariable = cs.alloc( - || "field division", - || div_value.ok_or(SynthesisError::AssignmentMissing), - )?; - let fe_2_inverse_value = fe_2_value.map(|v| v.inverse().unwrap()); - let fe_2_inverse_variable = cs.alloc( - || "field inverse", - || fe_2_inverse_value.ok_or(SynthesisError::AssignmentMissing), - )?; - - cs.enforce( - || "div = fe_1 * fe_2^-1", - |lc| lc + fe_1_variable, - |lc| lc + fe_2_inverse_variable, - |lc| lc + div_variable.clone(), - ); - - ConstrainedValue::FieldElement(FieldElement::Allocated(div_value, div_variable)) - } - }) - } - - pub(crate) fn enforce_field_pow( - &mut self, - cs: &mut CS, - fe_1: FieldElement, - num: Integer, - ) -> Result, FieldElementError> { - Ok(match fe_1 { - // if both constants, then return a constant result - FieldElement::Constant(fe_1_constant) => ConstrainedValue::FieldElement( - FieldElement::Constant(fe_1_constant.pow(&[num.to_usize() as u64])), - ), - // else, return an allocated result - FieldElement::Allocated(fe_1_value, _fe_1_variable) => { - let pow_value: Option = fe_1_value.map(|v| v.pow(&[num.to_usize() as u64])); - let pow_variable: R1CSVariable = cs.alloc( - || "field exponentiation", - || pow_value.ok_or(SynthesisError::AssignmentMissing), - )?; - - // cs.enforce( //todo: find a linear combination for this - // || "pow = 1 + fe_1^num", - // |lc| lc + fe_1_variable, - // |lc| lc + (fe_2_inverse_value, CS::one()), - // |lc| lc + pow_variable.clone()); - - ConstrainedValue::FieldElement(FieldElement::Allocated(pow_value, pow_variable)) - } - }) - } -} diff --git a/compiler/src/constraints/function.rs b/compiler/src/constraints/function.rs index b32aabf2ce..20382a369c 100644 --- a/compiler/src/constraints/function.rs +++ b/compiler/src/constraints/function.rs @@ -4,7 +4,7 @@ use crate::{ constraints::{new_scope, ConstrainedProgram, ConstrainedValue}, errors::{FunctionError, ImportError}, - group_from_input, + field_from_input, group_from_input, types::{Expression, Function, Identifier, InputValue, Program, Type}, GroupType, }; @@ -30,8 +30,8 @@ impl, CS: ConstraintSystem> Constraine scope: String, caller_scope: String, function_name: String, - expected_types: Vec>, - input: Expression, + expected_types: Vec, + input: Expression, ) -> Result, FunctionError> { // Evaluate the function input value as pass by value from the caller or // evaluate as an expression in the current function scope @@ -57,8 +57,8 @@ impl, CS: ConstraintSystem> Constraine cs: &mut CS, scope: String, caller_scope: String, - function: Function, - inputs: Vec>, + function: Function, + inputs: Vec, ) -> Result, FunctionError> { let function_name = new_scope(scope.clone(), function.get_name()); @@ -118,9 +118,9 @@ impl, CS: ConstraintSystem> Constraine cs: &mut CS, name: String, private: bool, - array_type: Type, + array_type: Type, array_dimensions: Vec, - input_value: Option>, + input_value: Option, ) -> Result, FunctionError> { let expected_length = array_dimensions[0]; let mut array_value = vec![]; @@ -170,18 +170,16 @@ impl, CS: ConstraintSystem> Constraine fn allocate_main_function_input( &mut self, cs: &mut CS, - _type: Type, + _type: Type, name: String, private: bool, - input_value: Option>, + input_value: Option, ) -> Result, FunctionError> { match _type { Type::IntegerType(integer_type) => { Ok(self.integer_from_parameter(cs, integer_type, name, private, input_value)?) } - Type::FieldElement => { - Ok(self.field_element_from_input(cs, name, private, input_value)?) - } + Type::Field => Ok(field_from_input(cs, name, private, input_value)?), Type::Group => Ok(group_from_input(cs, name, private, input_value)?), Type::Boolean => Ok(self.bool_from_input(cs, name, private, input_value)?), Type::Array(_type, dimensions) => { @@ -195,8 +193,8 @@ impl, CS: ConstraintSystem> Constraine &mut self, cs: &mut CS, scope: String, - function: Function, - inputs: Vec>>, + function: Function, + inputs: Vec>, ) -> Result, FunctionError> { let function_name = new_scope(scope.clone(), function.get_name()); @@ -231,7 +229,7 @@ impl, CS: ConstraintSystem> Constraine pub(crate) fn resolve_definitions( &mut self, cs: &mut CS, - program: Program, + program: Program, ) -> Result<(), ImportError> { let program_name = program.name.clone(); diff --git a/compiler/src/constraints/group.rs b/compiler/src/constraints/group.rs index 1f7583c5b8..5ed7dd4c81 100644 --- a/compiler/src/constraints/group.rs +++ b/compiler/src/constraints/group.rs @@ -10,7 +10,7 @@ pub(crate) fn group_from_input, CS: Const cs: &mut CS, name: String, private: bool, - input_value: Option>, + input_value: Option, ) -> Result, GroupError> { // Check that the parameter value is the correct type let group_option = match input_value { diff --git a/compiler/src/constraints/import.rs b/compiler/src/constraints/import.rs index d43f99f81e..0b032aacee 100644 --- a/compiler/src/constraints/import.rs +++ b/compiler/src/constraints/import.rs @@ -19,7 +19,7 @@ impl, CS: ConstraintSystem> Constraine &mut self, cs: &mut CS, scope: String, - import: Import, + import: Import, ) -> Result<(), ImportError> { let path = current_dir().map_err(|error| ImportError::DirectoryError(error))?; diff --git a/compiler/src/constraints/integer/integer.rs b/compiler/src/constraints/integer/integer.rs index cc44b5b99d..ea905b4ee2 100644 --- a/compiler/src/constraints/integer/integer.rs +++ b/compiler/src/constraints/integer/integer.rs @@ -105,7 +105,7 @@ impl, CS: ConstraintSystem> Constraine integer_type: IntegerType, name: String, private: bool, - integer_value: Option>, + integer_value: Option, ) -> Result, IntegerError> { // Check that the input value is the correct type let integer_option = match integer_value { diff --git a/compiler/src/constraints/mod.rs b/compiler/src/constraints/mod.rs index 761be73575..2cafbc1c89 100644 --- a/compiler/src/constraints/mod.rs +++ b/compiler/src/constraints/mod.rs @@ -15,8 +15,8 @@ pub use import::*; pub mod integer; pub use integer::*; -pub mod field_element; -pub use field_element::*; +pub(crate) mod field; +pub(crate) use field::*; pub(crate) mod group; pub(crate) use group::*; @@ -43,8 +43,8 @@ use snarkos_models::{ pub fn generate_constraints, CS: ConstraintSystem>( cs: &mut CS, - program: Program, - parameters: Vec>>, + program: Program, + parameters: Vec>, ) -> Result, CompilerError> { let mut resolved_program = ConstrainedProgram::new(); let program_name = program.get_name(); diff --git a/compiler/src/constraints/statement.rs b/compiler/src/constraints/statement.rs index 7cfdacc2bf..fb5fb63920 100644 --- a/compiler/src/constraints/statement.rs +++ b/compiler/src/constraints/statement.rs @@ -13,11 +13,14 @@ use crate::{ use snarkos_models::{ curves::{Field, PrimeField}, - gadgets::{r1cs::ConstraintSystem, utilities::boolean::Boolean, utilities::uint32::UInt32}, + gadgets::{ + r1cs::ConstraintSystem, + utilities::{boolean::Boolean, eq::EqGadget, uint32::UInt32}, + }, }; impl, CS: ConstraintSystem> ConstrainedProgram { - fn resolve_assignee(&mut self, scope: String, assignee: Assignee) -> String { + fn resolve_assignee(&mut self, scope: String, assignee: Assignee) -> String { match assignee { Assignee::Identifier(name) => new_scope(scope, name.to_string()), Assignee::Array(array, _index) => self.resolve_assignee(scope, *array), @@ -47,7 +50,7 @@ impl, CS: ConstraintSystem> Constraine file_scope: String, function_scope: String, name: String, - range_or_expression: RangeOrExpression, + range_or_expression: RangeOrExpression, new_value: ConstrainedValue, ) -> Result<(), StatementError> { // Resolve index so we know if we are assigning to a single value or a range of values @@ -91,7 +94,7 @@ impl, CS: ConstraintSystem> Constraine fn mutute_circuit_field( &mut self, circuit_name: String, - object_name: Identifier, + object_name: Identifier, new_value: ConstrainedValue, ) -> Result<(), StatementError> { match self.get_mutable_assignee(circuit_name)? { @@ -129,8 +132,8 @@ impl, CS: ConstraintSystem> Constraine cs: &mut CS, file_scope: String, function_scope: String, - assignee: Assignee, - expression: Expression, + assignee: Assignee, + expression: Expression, ) -> Result<(), StatementError> { // Get the name of the variable we are assigning to let variable_name = self.resolve_assignee(function_scope.clone(), assignee.clone()); @@ -170,7 +173,7 @@ impl, CS: ConstraintSystem> Constraine fn store_definition( &mut self, function_scope: String, - variable: Variable, + variable: Variable, mut value: ConstrainedValue, ) -> Result<(), StatementError> { // Store with given mutability @@ -190,8 +193,8 @@ impl, CS: ConstraintSystem> Constraine cs: &mut CS, file_scope: String, function_scope: String, - variable: Variable, - expression: Expression, + variable: Variable, + expression: Expression, ) -> Result<(), StatementError> { let mut expected_types = vec![]; if let Some(ref _type) = variable._type { @@ -213,8 +216,8 @@ impl, CS: ConstraintSystem> Constraine cs: &mut CS, file_scope: String, function_scope: String, - variables: Vec>, - function: Expression, + variables: Vec, + function: Expression, ) -> Result<(), StatementError> { let mut expected_types = vec![]; for variable in variables.iter() { @@ -256,8 +259,8 @@ impl, CS: ConstraintSystem> Constraine cs: &mut CS, file_scope: String, function_scope: String, - expressions: Vec>, - return_types: Vec>, + expressions: Vec, + return_types: Vec, ) -> Result, StatementError> { // Make sure we return the correct number of values if return_types.len() != expressions.len() { @@ -289,8 +292,8 @@ impl, CS: ConstraintSystem> Constraine cs: &mut CS, file_scope: String, function_scope: String, - statements: Vec>, - return_types: Vec>, + statements: Vec, + return_types: Vec, ) -> Result>, StatementError> { let mut res = None; // Evaluate statements and possibly return early @@ -315,8 +318,8 @@ impl, CS: ConstraintSystem> Constraine cs: &mut CS, file_scope: String, function_scope: String, - statement: ConditionalStatement, - return_types: Vec>, + statement: ConditionalStatement, + return_types: Vec, ) -> Result>, StatementError> { let expected_types = vec![Type::Boolean]; let condition = match self.enforce_expression( @@ -367,11 +370,11 @@ impl, CS: ConstraintSystem> Constraine cs: &mut CS, file_scope: String, function_scope: String, - index: Identifier, + index: Identifier, start: Integer, stop: Integer, - statements: Vec>, - return_types: Vec>, + statements: Vec, + return_types: Vec, ) -> Result>, StatementError> { let mut res = None; @@ -413,8 +416,8 @@ impl, CS: ConstraintSystem> Constraine (ConstrainedValue::Integer(num_1), ConstrainedValue::Integer(num_2)) => { Self::enforce_integer_eq(cs, num_1, num_2)? } - (ConstrainedValue::FieldElement(fe_1), ConstrainedValue::FieldElement(fe_2)) => { - self.enforce_field_eq(cs, fe_1, fe_2) + (ConstrainedValue::Field(fe_1), ConstrainedValue::Field(fe_2)) => { + fe_1.enforce_equal(cs, &fe_2)? } (ConstrainedValue::Group(ge_1), ConstrainedValue::Group(ge_2)) => { ge_1.enforce_equal(cs, &ge_2)? @@ -424,17 +427,11 @@ impl, CS: ConstraintSystem> Constraine self.enforce_assert_eq_statement(cs, left, right)?; } } - (val_1, val_2) => { - unimplemented!( - "assert eq not supported for given types {} == {}", - val_1, - val_2 - ) - // return Err(StatementError::AssertEq( - // val_1.to_string(), - // val_2.to_string(), - // )) - } + (val_1, val_2) => unimplemented!( + "assert eq not supported for given types {} == {}", + val_1, + val_2 + ), }) } @@ -443,8 +440,8 @@ impl, CS: ConstraintSystem> Constraine cs: &mut CS, file_scope: String, function_scope: String, - statement: Statement, - return_types: Vec>, + statement: Statement, + return_types: Vec, ) -> Result>, StatementError> { let mut res = None; match statement { diff --git a/compiler/src/constraints/value.rs b/compiler/src/constraints/value.rs index 60cd047fe6..f74ea6615d 100644 --- a/compiler/src/constraints/value.rs +++ b/compiler/src/constraints/value.rs @@ -2,8 +2,8 @@ use crate::{ errors::ValueError, - types::{Circuit, FieldElement, Function, Identifier, Integer, IntegerType, Type}, - GroupType, + types::{Circuit, Function, Identifier, Integer, IntegerType, Type}, + FieldType, GroupType, }; use snarkos_models::{ @@ -17,23 +17,23 @@ use std::fmt; #[derive(Clone, PartialEq, Eq)] pub struct ConstrainedCircuitMember>( - pub Identifier, + pub Identifier, pub ConstrainedValue, ); #[derive(Clone, PartialEq, Eq)] pub enum ConstrainedValue> { Integer(Integer), - FieldElement(FieldElement), + Field(FieldType), Group(G), Boolean(Boolean), Array(Vec>), - CircuitDefinition(Circuit), - CircuitExpression(Identifier, Vec>), + CircuitDefinition(Circuit), + CircuitExpression(Identifier, Vec>), - Function(Option>, Function), // (optional circuit identifier, function definition) + Function(Option, Function), // (optional circuit identifier, function definition) Return(Vec>), Mutable(Box>), @@ -51,7 +51,7 @@ impl> ConstrainedValue { ConstrainedValue::from_type(value, &other_type) } - pub(crate) fn from_type(value: String, _type: &Type) -> Result { + pub(crate) fn from_type(value: String, _type: &Type) -> Result { match _type { Type::IntegerType(integer_type) => Ok(ConstrainedValue::Integer(match integer_type { IntegerType::U8 => Integer::U8(UInt8::constant(value.parse::()?)), @@ -60,9 +60,7 @@ impl> ConstrainedValue { IntegerType::U64 => Integer::U64(UInt64::constant(value.parse::()?)), IntegerType::U128 => Integer::U128(UInt128::constant(value.parse::()?)), })), - Type::FieldElement => Ok(ConstrainedValue::FieldElement(FieldElement::Constant( - F::from_str(&value).unwrap_or_default(), - ))), + Type::Field => Ok(ConstrainedValue::Field(FieldType::constant(value)?)), Type::Group => Ok(ConstrainedValue::Group(G::constant(value)?)), Type::Boolean => Ok(ConstrainedValue::Boolean(Boolean::Constant( value.parse::()?, @@ -72,17 +70,17 @@ impl> ConstrainedValue { } } - pub(crate) fn to_type(&self) -> Type { + pub(crate) fn to_type(&self) -> Type { match self { ConstrainedValue::Integer(integer) => Type::IntegerType(integer.get_type()), - ConstrainedValue::FieldElement(_field) => Type::FieldElement, + ConstrainedValue::Field(_field) => Type::Field, ConstrainedValue::Group(_group) => Type::Group, ConstrainedValue::Boolean(_bool) => Type::Boolean, _ => unimplemented!("to type only implemented for primitives"), } } - pub(crate) fn resolve_type(&mut self, types: &Vec>) -> Result<(), ValueError> { + pub(crate) fn resolve_type(&mut self, types: &Vec) -> Result<(), ValueError> { if let ConstrainedValue::Unresolved(ref string) = self { if !types.is_empty() { *self = ConstrainedValue::from_type(string.clone(), &types[0])? @@ -103,7 +101,7 @@ impl> fmt::Display for ConstrainedValue fmt::Result { match *self { ConstrainedValue::Integer(ref value) => write!(f, "{}", value), - ConstrainedValue::FieldElement(ref value) => write!(f, "{}", value), + ConstrainedValue::Field(ref value) => write!(f, "{:?}", value), ConstrainedValue::Group(ref value) => write!(f, "{:?}", value), ConstrainedValue::Boolean(ref value) => write!(f, "{}", value.get_value().unwrap()), ConstrainedValue::Array(ref array) => { diff --git a/compiler/src/errors/constraints/expression.rs b/compiler/src/errors/constraints/expression.rs index 0201782d4d..c2e52212f6 100644 --- a/compiler/src/errors/constraints/expression.rs +++ b/compiler/src/errors/constraints/expression.rs @@ -1,5 +1,5 @@ use crate::errors::{ - BooleanError, FieldElementError, FunctionError, GroupError, IntegerError, ValueError, + BooleanError, FieldError, FunctionError, GroupError, IntegerError, ValueError, }; use snarkos_errors::gadgets::SynthesisError; @@ -28,7 +28,7 @@ pub enum ExpressionError { ParseIntError(ParseIntError), #[error("{}", _0)] - FieldElementError(FieldElementError), + FieldError(FieldError), #[error("{}", _0)] GroupError(#[from] GroupError), @@ -128,9 +128,9 @@ impl From for ExpressionError { } } -impl From for ExpressionError { - fn from(error: FieldElementError) -> Self { - ExpressionError::FieldElementError(error) +impl From for ExpressionError { + fn from(error: FieldError) -> Self { + ExpressionError::FieldError(error) } } diff --git a/compiler/src/errors/constraints/field_element.rs b/compiler/src/errors/constraints/field.rs similarity index 53% rename from compiler/src/errors/constraints/field_element.rs rename to compiler/src/errors/constraints/field.rs index 530a1a41fc..5582530e0c 100644 --- a/compiler/src/errors/constraints/field_element.rs +++ b/compiler/src/errors/constraints/field.rs @@ -1,16 +1,19 @@ use snarkos_errors::gadgets::SynthesisError; #[derive(Debug, Error)] -pub enum FieldElementError { +pub enum FieldError { #[error("Expected field element parameter, got {}", _0)] - InvalidField(String), + Invalid(String), + + #[error("No multiplicative inverse found for field {}", _0)] + NoInverse(String), #[error("{}", _0)] SynthesisError(SynthesisError), } -impl From for FieldElementError { +impl From for FieldError { fn from(error: SynthesisError) -> Self { - FieldElementError::SynthesisError(error) + FieldError::SynthesisError(error) } } diff --git a/compiler/src/errors/constraints/function.rs b/compiler/src/errors/constraints/function.rs index 21278a5eb8..d73cbd2944 100644 --- a/compiler/src/errors/constraints/function.rs +++ b/compiler/src/errors/constraints/function.rs @@ -1,6 +1,5 @@ use crate::errors::{ - BooleanError, ExpressionError, FieldElementError, GroupError, IntegerError, StatementError, - ValueError, + BooleanError, ExpressionError, FieldError, GroupError, IntegerError, StatementError, ValueError, }; #[derive(Debug, Error)] @@ -27,7 +26,7 @@ pub enum FunctionError { IntegerError(IntegerError), #[error("{}", _0)] - FieldElementError(FieldElementError), + FieldError(FieldError), #[error("{}", _0)] GroupError(GroupError), @@ -54,9 +53,9 @@ impl From for FunctionError { } } -impl From for FunctionError { - fn from(error: FieldElementError) -> Self { - FunctionError::FieldElementError(error) +impl From for FunctionError { + fn from(error: FieldError) -> Self { + FunctionError::FieldError(error) } } diff --git a/compiler/src/errors/constraints/mod.rs b/compiler/src/errors/constraints/mod.rs index 2a826c241f..163fa1c517 100644 --- a/compiler/src/errors/constraints/mod.rs +++ b/compiler/src/errors/constraints/mod.rs @@ -15,8 +15,8 @@ pub use import::*; pub mod integer; pub use integer::*; -pub mod field_element; -pub use field_element::*; +pub mod field; +pub use field::*; pub mod group; pub use group::*; diff --git a/compiler/src/errors/constraints/statement.rs b/compiler/src/errors/constraints/statement.rs index 006317609f..62ac343972 100644 --- a/compiler/src/errors/constraints/statement.rs +++ b/compiler/src/errors/constraints/statement.rs @@ -1,4 +1,4 @@ -use crate::errors::{BooleanError, ExpressionError, FieldElementError, IntegerError, ValueError}; +use crate::errors::{BooleanError, ExpressionError, FieldError, IntegerError, ValueError}; use snarkos_errors::gadgets::SynthesisError; @@ -14,7 +14,7 @@ pub enum StatementError { IntegerError(IntegerError), #[error("{}", _0)] - FieldElementError(FieldElementError), + FieldError(FieldError), #[error("{}", _0)] BooleanError(BooleanError), @@ -81,9 +81,9 @@ impl From for StatementError { } } -impl From for StatementError { - fn from(error: FieldElementError) -> Self { - StatementError::FieldElementError(error) +impl From for StatementError { + fn from(error: FieldError) -> Self { + StatementError::FieldError(error) } } diff --git a/compiler/src/errors/constraints/value.rs b/compiler/src/errors/constraints/value.rs index 91e1ff68f4..7b1d146309 100644 --- a/compiler/src/errors/constraints/value.rs +++ b/compiler/src/errors/constraints/value.rs @@ -1,4 +1,4 @@ -use crate::errors::{GroupError, IntegerError}; +use crate::errors::{FieldError, GroupError, IntegerError}; use std::{num::ParseIntError, str::ParseBoolError}; @@ -29,6 +29,9 @@ pub enum ValueError { #[error("{}", _0)] GroupError(#[from] GroupError), + + #[error("{}", _0)] + FieldError(#[from] FieldError), } impl From for ValueError { diff --git a/compiler/src/field/mod.rs b/compiler/src/field/mod.rs new file mode 100644 index 0000000000..63e7573b7c --- /dev/null +++ b/compiler/src/field/mod.rs @@ -0,0 +1,289 @@ +//! A data type that represents a field value + +use crate::errors::FieldError; + +use snarkos_errors::gadgets::SynthesisError; +use snarkos_models::gadgets::curves::FieldGadget; +use snarkos_models::{ + curves::{Field, PrimeField}, + gadgets::{ + curves::FpGadget, + r1cs::ConstraintSystem, + utilities::{ + alloc::AllocGadget, + boolean::Boolean, + eq::{ConditionalEqGadget, EqGadget}, + select::CondSelectGadget, + uint8::UInt8, + ToBitsGadget, ToBytesGadget, + }, + }, +}; +use std::borrow::Borrow; +use std::cmp::Ordering; + +#[derive(Clone, Debug)] +pub enum FieldType { + Constant(F), + Allocated(FpGadget), +} + +impl FieldType { + pub fn get_value(&self) -> Option { + match self { + FieldType::Constant(field) => Some(*field), + FieldType::Allocated(gadget) => gadget.get_value(), + } + } + + pub fn constant(string: String) -> Result { + let value = F::from_str(&string).map_err(|_| FieldError::Invalid(string))?; + + Ok(FieldType::Constant(value)) + } + + pub fn add>(&self, cs: CS, other: &Self) -> Result { + match (self, other) { + (FieldType::Constant(self_value), FieldType::Constant(other_value)) => { + Ok(FieldType::Constant(self_value.add(other_value))) + } + + (FieldType::Allocated(self_value), FieldType::Allocated(other_value)) => { + let result = self_value.add(cs, other_value)?; + + Ok(FieldType::Allocated(result)) + } + + (FieldType::Constant(constant_value), FieldType::Allocated(allocated_value)) + | (FieldType::Allocated(allocated_value), FieldType::Constant(constant_value)) => Ok( + FieldType::Allocated(allocated_value.add_constant(cs, constant_value)?), + ), + } + } + + pub fn sub>(&self, cs: CS, other: &Self) -> Result { + match (self, other) { + (FieldType::Constant(self_value), FieldType::Constant(other_value)) => { + Ok(FieldType::Constant(self_value.sub(other_value))) + } + + (FieldType::Allocated(self_value), FieldType::Allocated(other_value)) => { + let result = self_value.sub(cs, other_value)?; + + Ok(FieldType::Allocated(result)) + } + + (FieldType::Constant(constant_value), FieldType::Allocated(allocated_value)) + | (FieldType::Allocated(allocated_value), FieldType::Constant(constant_value)) => Ok( + FieldType::Allocated(allocated_value.sub_constant(cs, constant_value)?), + ), + } + } + + pub fn mul>(&self, cs: CS, other: &Self) -> Result { + match (self, other) { + (FieldType::Constant(self_value), FieldType::Constant(other_value)) => { + Ok(FieldType::Constant(self_value.mul(other_value))) + } + + (FieldType::Allocated(self_value), FieldType::Allocated(other_value)) => { + let result = self_value.mul(cs, other_value)?; + + Ok(FieldType::Allocated(result)) + } + + (FieldType::Constant(constant_value), FieldType::Allocated(allocated_value)) + | (FieldType::Allocated(allocated_value), FieldType::Constant(constant_value)) => Ok( + FieldType::Allocated(allocated_value.mul_by_constant(cs, constant_value)?), + ), + } + } + + pub fn div>( + &self, + mut cs: CS, + other: &Self, + ) -> Result { + let inverse = match other { + FieldType::Constant(constant) => { + let constant_inverse = constant + .inverse() + .ok_or(FieldError::NoInverse(constant.to_string()))?; + FieldType::Constant(constant_inverse) + } + FieldType::Allocated(allocated) => { + let allocated_inverse = allocated.inverse(&mut cs)?; + FieldType::Allocated(allocated_inverse) + } + }; + + self.mul(cs, &inverse) + } + + pub fn alloc_helper Result, T: Borrow>( + value_gen: Fn, + ) -> Result { + let field_string = match value_gen() { + Ok(value) => { + let string_value = value.borrow().clone(); + Ok(string_value) + } + _ => Err(SynthesisError::AssignmentMissing), + }?; + + F::from_str(&field_string).map_err(|_| SynthesisError::AssignmentMissing) + } + + pub fn allocated>( + &self, + mut cs: CS, + ) -> Result, SynthesisError> { + match self { + FieldType::Constant(constant) => { + FpGadget::alloc(&mut cs.ns(|| format!("{:?}", constant)), || Ok(constant)) + } + FieldType::Allocated(allocated) => Ok(allocated.clone()), + } + } +} + +impl AllocGadget for FieldType { + fn alloc< + Fn: FnOnce() -> Result, + T: Borrow, + CS: ConstraintSystem, + >( + cs: CS, + value_gen: Fn, + ) -> Result { + let value = FpGadget::alloc(cs, || Self::alloc_helper(value_gen))?; + + Ok(FieldType::Allocated(value)) + } + + fn alloc_input< + Fn: FnOnce() -> Result, + T: Borrow, + CS: ConstraintSystem, + >( + cs: CS, + value_gen: Fn, + ) -> Result { + let value = FpGadget::alloc_input(cs, || Self::alloc_helper(value_gen))?; + + Ok(FieldType::Allocated(value)) + } +} + +impl PartialEq for FieldType { + fn eq(&self, other: &Self) -> bool { + let self_value = self.get_value(); + let other_value = other.get_value(); + + self_value.is_some() && other_value.is_some() && self_value.eq(&other_value) + } +} + +impl Eq for FieldType {} + +impl PartialOrd for FieldType { + fn partial_cmp(&self, other: &Self) -> Option { + let self_value = self.get_value(); + let other_value = other.get_value(); + + Option::from(self_value.cmp(&other_value)) + } +} + +impl EqGadget for FieldType {} + +impl ConditionalEqGadget for FieldType { + fn conditional_enforce_equal>( + &self, + mut cs: CS, + other: &Self, + condition: &Boolean, + ) -> Result<(), SynthesisError> { + match (self, other) { + // c - c + (FieldType::Constant(self_value), FieldType::Constant(other_value)) => { + if self_value == other_value { + return Ok(()); + } + Err(SynthesisError::AssignmentMissing) + } + // a - a + (FieldType::Allocated(self_value), FieldType::Allocated(other_value)) => { + self_value.conditional_enforce_equal(cs, other_value, condition) + } + // c - a = a - c + (FieldType::Constant(constant_value), FieldType::Allocated(allocated_value)) + | (FieldType::Allocated(allocated_value), FieldType::Constant(constant_value)) => { + let constant_gadget = FpGadget::from(&mut cs, constant_value); + + constant_gadget.conditional_enforce_equal(cs, allocated_value, condition) + } + } + } + + fn cost() -> usize { + 2 * as ConditionalEqGadget>::cost() + } +} + +impl CondSelectGadget for FieldType { + fn conditionally_select>( + mut cs: CS, + cond: &Boolean, + first: &Self, + second: &Self, + ) -> Result { + if let Boolean::Constant(cond) = *cond { + if cond { + Ok(first.clone()) + } else { + Ok(second.clone()) + } + } else { + let first_gadget = first.allocated(&mut cs)?; + let second_gadget = second.allocated(&mut cs)?; + let result = FpGadget::conditionally_select(cs, cond, &first_gadget, &second_gadget)?; + + Ok(FieldType::Allocated(result)) + } + } + + fn cost() -> usize { + 2 * as CondSelectGadget>::cost() + } +} + +impl ToBitsGadget for FieldType { + fn to_bits>(&self, mut cs: CS) -> Result, SynthesisError> { + let self_gadget = self.allocated(&mut cs)?; + self_gadget.to_bits(cs) + } + + fn to_bits_strict>( + &self, + mut cs: CS, + ) -> Result, SynthesisError> { + let self_gadget = self.allocated(&mut cs)?; + self_gadget.to_bits_strict(cs) + } +} + +impl ToBytesGadget for FieldType { + fn to_bytes>(&self, mut cs: CS) -> Result, SynthesisError> { + let self_gadget = self.allocated(&mut cs)?; + self_gadget.to_bytes(cs) + } + + fn to_bytes_strict>( + &self, + mut cs: CS, + ) -> Result, SynthesisError> { + let self_gadget = self.allocated(&mut cs)?; + self_gadget.to_bytes_strict(cs) + } +} diff --git a/compiler/src/group/edwards_bls12.rs b/compiler/src/group/edwards_bls12.rs index b79dc03663..0b1ac1a7c3 100644 --- a/compiler/src/group/edwards_bls12.rs +++ b/compiler/src/group/edwards_bls12.rs @@ -214,7 +214,7 @@ impl ConditionalEqGadget for EdwardsGroupType { } // a - a (EdwardsGroupType::Allocated(self_value), EdwardsGroupType::Allocated(other_value)) => { - return ::conditional_enforce_equal( + ::conditional_enforce_equal( self_value, cs, other_value, diff --git a/compiler/src/group/mod.rs b/compiler/src/group/mod.rs index 777909c824..c29973b4dc 100644 --- a/compiler/src/group/mod.rs +++ b/compiler/src/group/mod.rs @@ -1,3 +1,5 @@ +//! A data type that represents members in the group formed by the set of affine points on a curve. + use crate::errors::GroupError; use snarkos_models::{ diff --git a/compiler/src/imports.rs b/compiler/src/imports.rs index d863f76eec..383606315f 100644 --- a/compiler/src/imports.rs +++ b/compiler/src/imports.rs @@ -2,23 +2,22 @@ use crate::Identifier; -use snarkos_models::curves::{Field, PrimeField}; use std::fmt; #[derive(Clone)] -pub struct ImportSymbol { - pub symbol: Identifier, - pub alias: Option>, +pub struct ImportSymbol { + pub symbol: Identifier, + pub alias: Option, } #[derive(Clone)] -pub struct Import { +pub struct Import { pub path_string: String, - pub symbols: Vec>, + pub symbols: Vec, } -impl Import { - pub fn new(source: String, symbols: Vec>) -> Import { +impl Import { + pub fn new(source: String, symbols: Vec) -> Import { Import { path_string: source, symbols, @@ -51,7 +50,7 @@ impl Import { } } -impl fmt::Display for ImportSymbol { +impl fmt::Display for ImportSymbol { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { if self.alias.is_some() { write!(f, "\t{} as {}", self.symbol, self.alias.as_ref().unwrap()) @@ -61,13 +60,13 @@ impl fmt::Display for ImportSymbol { } } -impl<'ast, F: Field + PrimeField> fmt::Display for Import { +impl<'ast> fmt::Display for Import { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.format(f) } } -impl<'ast, F: Field + PrimeField> fmt::Debug for Import { +impl<'ast> fmt::Debug for Import { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.format(f) } diff --git a/compiler/src/leo.pest b/compiler/src/leo.pest index 454e70d991..fd2ab2c56c 100644 --- a/compiler/src/leo.pest +++ b/compiler/src/leo.pest @@ -25,11 +25,11 @@ operation_and = { "&&" } operation_or = { "||" } operation_eq = { "==" } -operation_neq = { "!=" } +operation_ne = { "!=" } -operation_geq = { ">=" } +operation_ge = { ">=" } operation_gt = { ">" } -operation_leq = { "<=" } +operation_le = { "<=" } operation_lt = { "<" } operation_add = { "+" } @@ -39,9 +39,9 @@ operation_div = { "/" } operation_pow = { "**" } operation_compare = _{ - operation_eq | operation_neq | - operation_geq | operation_gt | - operation_leq | operation_lt + operation_eq | operation_ne | + operation_ge | operation_gt | + operation_le | operation_lt } operation_binary = _{ diff --git a/compiler/src/lib.rs b/compiler/src/lib.rs index 7c767b3b89..06993c1d40 100644 --- a/compiler/src/lib.rs +++ b/compiler/src/lib.rs @@ -22,6 +22,9 @@ pub use self::constraints::*; pub mod errors; +pub mod field; +pub use self::field::*; + pub mod group; pub use self::group::*; diff --git a/compiler/src/types.rs b/compiler/src/types.rs index ccf3e1fc86..89c909c26d 100644 --- a/compiler/src/types.rs +++ b/compiler/src/types.rs @@ -3,31 +3,21 @@ use crate::Import; -use snarkos_models::{ - curves::{Field, PrimeField}, - gadgets::{ - r1cs::Variable as R1CSVariable, - utilities::{ - boolean::Boolean, uint128::UInt128, uint16::UInt16, uint32::UInt32, uint64::UInt64, - uint8::UInt8, - }, - }, +use snarkos_models::gadgets::utilities::{ + boolean::Boolean, uint128::UInt128, uint16::UInt16, uint32::UInt32, uint64::UInt64, + uint8::UInt8, }; -use std::{collections::HashMap, marker::PhantomData}; +use std::collections::HashMap; /// An identifier in the constrained program. #[derive(Clone, PartialEq, Eq, Hash)] -pub struct Identifier { +pub struct Identifier { pub name: String, - pub(crate) _engine: PhantomData, } -impl Identifier { +impl Identifier { pub fn new(name: String) -> Self { - Self { - name, - _engine: PhantomData::, - } + Self { name } } pub fn is_self(&self) -> bool { @@ -37,10 +27,10 @@ impl Identifier { /// A variable that is assigned to a value in the constrained program #[derive(Clone, PartialEq, Eq)] -pub struct Variable { - pub identifier: Identifier, +pub struct Variable { + pub identifier: Identifier, pub mutable: bool, - pub _type: Option>, + pub _type: Option, } /// An integer type enum wrapping the integer value. Used only in expressions. @@ -53,79 +43,72 @@ pub enum Integer { U128(UInt128), } -/// A constant or allocated element in the field -#[derive(Clone, PartialEq, Eq)] -pub enum FieldElement { - Constant(F), - Allocated(Option, R1CSVariable), -} - /// Range or expression enum #[derive(Debug, Clone, PartialEq, Eq)] -pub enum RangeOrExpression { +pub enum RangeOrExpression { Range(Option, Option), - Expression(Expression), + Expression(Expression), } /// Spread or expression #[derive(Debug, Clone, PartialEq, Eq)] -pub enum SpreadOrExpression { - Spread(Expression), - Expression(Expression), +pub enum SpreadOrExpression { + Spread(Expression), + Expression(Expression), } /// Expression that evaluates to a value #[derive(Debug, Clone, PartialEq, Eq)] -pub enum Expression { +pub enum Expression { // Identifier - Identifier(Identifier), + Identifier(Identifier), // Values Integer(Integer), - FieldElement(FieldElement), + Field(String), Group(String), Boolean(Boolean), Implicit(String), // Number operations - Add(Box>, Box>), - Sub(Box>, Box>), - Mul(Box>, Box>), - Div(Box>, Box>), - Pow(Box>, Box>), + Add(Box, Box), + Sub(Box, Box), + Mul(Box, Box), + Div(Box, Box), + Pow(Box, Box), // Boolean operations - Not(Box>), - Or(Box>, Box>), - And(Box>, Box>), - Eq(Box>, Box>), - Geq(Box>, Box>), - Gt(Box>, Box>), - Leq(Box>, Box>), - Lt(Box>, Box>), + Not(Box), + Or(Box, Box), + And(Box, Box), + Eq(Box, Box), + Ge(Box, Box), + Gt(Box, Box), + Le(Box, Box), + Lt(Box, Box), // Conditionals - IfElse(Box>, Box>, Box>), + IfElse(Box, Box, Box), // Arrays - Array(Vec>>), - ArrayAccess(Box>, Box>), // (array name, range) + Array(Vec>), + ArrayAccess(Box, Box), // (array name, range) // Circuits - Circuit(Identifier, Vec>), - CircuitMemberAccess(Box>, Identifier), // (declared circuit name, circuit member name) - CircuitStaticFunctionAccess(Box>, Identifier), // (defined circuit name, circuit static member name) + Circuit(Identifier, Vec), + CircuitMemberAccess(Box, Identifier), // (declared circuit name, circuit member name) + CircuitStaticFunctionAccess(Box, Identifier), // (defined circuit name, circuit static member name) // Functions - FunctionCall(Box>, Vec>), + FunctionCall(Box, Vec), } /// Definition assignee: v, arr[0..2], Point p.x #[derive(Debug, Clone, PartialEq, Eq)] -pub enum Assignee { - Identifier(Identifier), - Array(Box>, RangeOrExpression), - CircuitField(Box>, Identifier), // (circuit name, circuit field name) +pub enum Assignee { + Identifier(Identifier), + Array(Box, RangeOrExpression), + CircuitField(Box, Identifier), // (circuit name, circuit field name) } /// Explicit integer type @@ -140,17 +123,17 @@ pub enum IntegerType { /// Explicit type used for defining a variable or expression type #[derive(Clone, Debug, PartialEq, Eq)] -pub enum Type { +pub enum Type { IntegerType(IntegerType), - FieldElement, + Field, Group, Boolean, - Array(Box>, Vec), - Circuit(Identifier), + Array(Box, Vec), + Circuit(Identifier), SelfType, } -impl Type { +impl Type { pub fn outer_dimension(&self, dimensions: &Vec) -> Self { let _type = self.clone(); @@ -179,79 +162,79 @@ impl Type { } #[derive(Clone, PartialEq, Eq)] -pub enum ConditionalNestedOrEnd { - Nested(Box>), - End(Vec>), +pub enum ConditionalNestedOrEnd { + Nested(Box), + End(Vec), } #[derive(Clone, PartialEq, Eq)] -pub struct ConditionalStatement { - pub condition: Expression, - pub statements: Vec>, - pub next: Option>, +pub struct ConditionalStatement { + pub condition: Expression, + pub statements: Vec, + pub next: Option, } /// Program statement that defines some action (or expression) to be carried out. #[derive(Clone, PartialEq, Eq)] -pub enum Statement { - Return(Vec>), - Definition(Variable, Expression), - Assign(Assignee, Expression), - MultipleAssign(Vec>, Expression), - Conditional(ConditionalStatement), - For(Identifier, Integer, Integer, Vec>), - AssertEq(Expression, Expression), - Expression(Expression), +pub enum Statement { + Return(Vec), + Definition(Variable, Expression), + Assign(Assignee, Expression), + MultipleAssign(Vec, Expression), + Conditional(ConditionalStatement), + For(Identifier, Integer, Integer, Vec), + AssertEq(Expression, Expression), + Expression(Expression), } /// Circuits #[derive(Clone, Debug, PartialEq, Eq)] -pub struct CircuitFieldDefinition { - pub identifier: Identifier, - pub expression: Expression, +pub struct CircuitFieldDefinition { + pub identifier: Identifier, + pub expression: Expression, } #[derive(Clone, PartialEq, Eq)] -pub enum CircuitMember { - CircuitField(Identifier, Type), - CircuitFunction(bool, Function), +pub enum CircuitMember { + CircuitField(Identifier, Type), + CircuitFunction(bool, Function), } #[derive(Clone, PartialEq, Eq)] -pub struct Circuit { - pub identifier: Identifier, - pub members: Vec>, +pub struct Circuit { + pub identifier: Identifier, + pub members: Vec, } /// Function parameters #[derive(Clone, PartialEq, Eq)] -pub struct InputModel { - pub identifier: Identifier, +pub struct InputModel { + pub identifier: Identifier, pub mutable: bool, pub private: bool, - pub _type: Type, + pub _type: Type, } #[derive(Clone, PartialEq, Eq)] -pub enum InputValue { +pub enum InputValue { Integer(usize), - Field(F), + Field(String), Group(String), Boolean(bool), - Array(Vec>), + Array(Vec), } #[derive(Clone, PartialEq, Eq)] -pub struct Function { - pub function_name: Identifier, - pub inputs: Vec>, - pub returns: Vec>, - pub statements: Vec>, +pub struct Function { + pub function_name: Identifier, + pub inputs: Vec, + pub returns: Vec, + pub statements: Vec, } -impl Function { +impl Function { pub fn get_name(&self) -> String { self.function_name.name.clone() } @@ -259,15 +242,15 @@ impl Function { /// A simple program with statement expressions, program arguments and program returns. #[derive(Debug, Clone)] -pub struct Program { - pub name: Identifier, +pub struct Program { + pub name: Identifier, pub num_parameters: usize, - pub imports: Vec>, - pub circuits: HashMap, Circuit>, - pub functions: HashMap, Function>, + pub imports: Vec, + pub circuits: HashMap, + pub functions: HashMap, } -impl<'ast, F: Field + PrimeField> Program { +impl<'ast> Program { pub fn new() -> Self { Self { name: Identifier::new("".into()), diff --git a/compiler/src/types_display.rs b/compiler/src/types_display.rs index 62c3e99817..2ae68b8651 100644 --- a/compiler/src/types_display.rs +++ b/compiler/src/types_display.rs @@ -2,25 +2,24 @@ use crate::{ Assignee, Circuit, CircuitMember, ConditionalNestedOrEnd, ConditionalStatement, Expression, - FieldElement, Function, Identifier, InputModel, InputValue, Integer, IntegerType, - RangeOrExpression, SpreadOrExpression, Statement, Type, Variable, + Function, Identifier, InputModel, InputValue, Integer, IntegerType, RangeOrExpression, + SpreadOrExpression, Statement, Type, Variable, }; -use snarkos_models::curves::{Field, PrimeField}; use std::fmt; -impl fmt::Display for Identifier { +impl fmt::Display for Identifier { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.name) } } -impl fmt::Debug for Identifier { +impl fmt::Debug for Identifier { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.name) } } -impl fmt::Display for Variable { +impl fmt::Display for Variable { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { if self.mutable { write!(f, "mut ")?; @@ -42,34 +41,7 @@ impl fmt::Display for Integer { } } -impl FieldElement { - fn format(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - FieldElement::Constant(ref constant) => write!(f, "{}", constant), - FieldElement::Allocated(ref option, ref _r1cs_var) => { - if option.is_some() { - write!(f, "{}", option.unwrap()) - } else { - write!(f, "allocated field") - } - } - } - } -} - -impl fmt::Display for FieldElement { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.format(f) - } -} - -impl fmt::Debug for FieldElement { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.format(f) - } -} - -impl<'ast, F: Field + PrimeField> fmt::Display for RangeOrExpression { +impl<'ast> fmt::Display for RangeOrExpression { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { RangeOrExpression::Range(ref from, ref to) => write!( @@ -87,7 +59,7 @@ impl<'ast, F: Field + PrimeField> fmt::Display for RangeOrExpression { } } -impl fmt::Display for SpreadOrExpression { +impl fmt::Display for SpreadOrExpression { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { SpreadOrExpression::Spread(ref spread) => write!(f, "...{}", spread), @@ -96,7 +68,7 @@ impl fmt::Display for SpreadOrExpression { } } -impl<'ast, F: Field + PrimeField> fmt::Display for Expression { +impl<'ast> fmt::Display for Expression { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { // Variables @@ -104,7 +76,7 @@ impl<'ast, F: Field + PrimeField> fmt::Display for Expression { // Values Expression::Integer(ref integer) => write!(f, "{}", integer), - Expression::FieldElement(ref field) => write!(f, "{}", field), + Expression::Field(ref field) => write!(f, "{}", field), Expression::Group(ref group) => write!(f, "{}", group), Expression::Boolean(ref bool) => write!(f, "{}", bool.get_value().unwrap()), Expression::Implicit(ref value) => write!(f, "{}", value), @@ -121,9 +93,9 @@ impl<'ast, F: Field + PrimeField> fmt::Display for Expression { Expression::Or(ref lhs, ref rhs) => write!(f, "{} || {}", lhs, rhs), Expression::And(ref lhs, ref rhs) => write!(f, "{} && {}", lhs, rhs), Expression::Eq(ref lhs, ref rhs) => write!(f, "{} == {}", lhs, rhs), - Expression::Geq(ref lhs, ref rhs) => write!(f, "{} >= {}", lhs, rhs), + Expression::Ge(ref lhs, ref rhs) => write!(f, "{} >= {}", lhs, rhs), Expression::Gt(ref lhs, ref rhs) => write!(f, "{} > {}", lhs, rhs), - Expression::Leq(ref lhs, ref rhs) => write!(f, "{} <= {}", lhs, rhs), + Expression::Le(ref lhs, ref rhs) => write!(f, "{} <= {}", lhs, rhs), Expression::Lt(ref lhs, ref rhs) => write!(f, "{} < {}", lhs, rhs), // Conditionals @@ -177,7 +149,7 @@ impl<'ast, F: Field + PrimeField> fmt::Display for Expression { } } -impl fmt::Display for Assignee { +impl fmt::Display for Assignee { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { Assignee::Identifier(ref variable) => write!(f, "{}", variable), @@ -189,7 +161,7 @@ impl fmt::Display for Assignee { } } -impl fmt::Display for ConditionalNestedOrEnd { +impl fmt::Display for ConditionalNestedOrEnd { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { ConditionalNestedOrEnd::Nested(ref nested) => write!(f, "else {}", nested), @@ -204,7 +176,7 @@ impl fmt::Display for ConditionalNestedOrEnd { } } -impl fmt::Display for ConditionalStatement { +impl fmt::Display for ConditionalStatement { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "if ({}) {{\n", self.condition)?; for statement in self.statements.iter() { @@ -217,7 +189,7 @@ impl fmt::Display for ConditionalStatement { } } -impl fmt::Display for Statement { +impl fmt::Display for Statement { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { Statement::Return(ref statements) => { @@ -274,11 +246,11 @@ impl fmt::Display for IntegerType { } } -impl fmt::Display for Type { +impl fmt::Display for Type { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { Type::IntegerType(ref integer_type) => write!(f, "{}", integer_type), - Type::FieldElement => write!(f, "field"), + Type::Field => write!(f, "field"), Type::Group => write!(f, "group"), Type::Boolean => write!(f, "bool"), Type::Circuit(ref variable) => write!(f, "{}", variable), @@ -294,7 +266,7 @@ impl fmt::Display for Type { } } -impl fmt::Display for CircuitMember { +impl fmt::Display for CircuitMember { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { CircuitMember::CircuitField(ref identifier, ref _type) => { @@ -310,7 +282,7 @@ impl fmt::Display for CircuitMember { } } -impl Circuit { +impl Circuit { fn format(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "circuit {} {{ \n", self.identifier)?; for field in self.members.iter() { @@ -320,19 +292,19 @@ impl Circuit { } } -// impl fmt::Display for Circuit {// uncomment when we no longer print out Program +// impl fmt::Display for Circuit {// uncomment when we no longer print out Program // fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { // self.format(f) // } // } -impl fmt::Debug for Circuit { +impl fmt::Debug for Circuit { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.format(f) } } -impl fmt::Display for InputModel { +impl fmt::Display for InputModel { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { // mut var: private bool if self.mutable { @@ -348,7 +320,7 @@ impl fmt::Display for InputModel { } } -impl fmt::Display for InputValue { +impl fmt::Display for InputValue { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { InputValue::Integer(ref integer) => write!(f, "{}", integer), @@ -369,7 +341,7 @@ impl fmt::Display for InputValue { } } -impl Function { +impl Function { fn format(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "function {}", self.function_name)?; let parameters = self @@ -400,13 +372,13 @@ impl Function { } } -impl fmt::Display for Function { +impl fmt::Display for Function { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.format(f) } } -impl fmt::Debug for Function { +impl fmt::Debug for Function { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.format(f) } diff --git a/compiler/src/types_from.rs b/compiler/src/types_from.rs index 3bffba3c7b..b81e435a36 100644 --- a/compiler/src/types_from.rs +++ b/compiler/src/types_from.rs @@ -2,24 +2,21 @@ use crate::{ast, types, Import, ImportSymbol}; -use snarkos_models::{ - curves::{Field, PrimeField}, - gadgets::utilities::{ - boolean::Boolean, uint128::UInt128, uint16::UInt16, uint32::UInt32, uint64::UInt64, - uint8::UInt8, - }, +use snarkos_models::gadgets::utilities::{ + boolean::Boolean, uint128::UInt128, uint16::UInt16, uint32::UInt32, uint64::UInt64, + uint8::UInt8, }; use std::collections::HashMap; /// pest ast -> types::Identifier -impl<'ast, F: Field + PrimeField> From> for types::Identifier { +impl<'ast> From> for types::Identifier { fn from(identifier: ast::Identifier<'ast>) -> Self { types::Identifier::new(identifier.value) } } -impl<'ast, F: Field + PrimeField> From> for types::Expression { +impl<'ast> From> for types::Expression { fn from(identifier: ast::Identifier<'ast>) -> Self { types::Expression::Identifier(types::Identifier::from(identifier)) } @@ -27,7 +24,7 @@ impl<'ast, F: Field + PrimeField> From> for types::Express /// pest ast -> types::Variable -impl<'ast, F: Field + PrimeField> From> for types::Variable { +impl<'ast> From> for types::Variable { fn from(variable: ast::Variable<'ast>) -> Self { types::Variable { identifier: types::Identifier::from(variable.identifier), @@ -70,21 +67,19 @@ impl<'ast> types::Integer { } } -impl<'ast, F: Field + PrimeField> From> for types::Expression { +impl<'ast> From> for types::Expression { fn from(field: ast::Integer<'ast>) -> Self { types::Expression::Integer(types::Integer::from(field.number, field._type)) } } -impl<'ast, F: Field + PrimeField> From> - for types::RangeOrExpression -{ +impl<'ast> From> for types::RangeOrExpression { fn from(range_or_expression: ast::RangeOrExpression<'ast>) -> Self { match range_or_expression { ast::RangeOrExpression::Range(range) => { let from = range .from - .map(|from| match types::Expression::::from(from.0) { + .map(|from| match types::Expression::from(from.0) { types::Expression::Integer(number) => number, types::Expression::Implicit(string) => { types::Integer::from_implicit(string) @@ -93,7 +88,7 @@ impl<'ast, F: Field + PrimeField> From> unimplemented!("Range bounds should be integers, found {}", expression) } }); - let to = range.to.map(|to| match types::Expression::::from(to.0) { + let to = range.to.map(|to| match types::Expression::from(to.0) { types::Expression::Integer(number) => number, types::Expression::Implicit(string) => types::Integer::from_implicit(string), expression => { @@ -112,17 +107,15 @@ impl<'ast, F: Field + PrimeField> From> /// pest ast -> types::Field -impl<'ast, F: Field + PrimeField> From> for types::Expression { +impl<'ast> From> for types::Expression { fn from(field: ast::Field<'ast>) -> Self { - types::Expression::FieldElement(types::FieldElement::Constant( - F::from_str(&field.number.value).unwrap_or_default(), - )) + types::Expression::Field(field.number.value) } } /// pest ast -> types::Group -impl<'ast, F: Field + PrimeField> From> for types::Expression { +impl<'ast> From> for types::Expression { fn from(group: ast::Group<'ast>) -> Self { types::Expression::Group(group.to_string()) } @@ -130,7 +123,7 @@ impl<'ast, F: Field + PrimeField> From> for types::Expression types::Boolean -impl<'ast, F: Field + PrimeField> From> for types::Expression { +impl<'ast> From> for types::Expression { fn from(boolean: ast::Boolean<'ast>) -> Self { types::Expression::Boolean(Boolean::Constant( boolean @@ -143,7 +136,7 @@ impl<'ast, F: Field + PrimeField> From> for types::Expression /// pest ast -> types::NumberImplicit -impl<'ast, F: Field + PrimeField> From> for types::Expression { +impl<'ast> From> for types::Expression { fn from(number: ast::NumberImplicit<'ast>) -> Self { types::Expression::Implicit(number.number.value) } @@ -151,7 +144,7 @@ impl<'ast, F: Field + PrimeField> From> for types::Exp /// pest ast -> types::Expression -impl<'ast, F: Field + PrimeField> From> for types::Expression { +impl<'ast> From> for types::Expression { fn from(value: ast::Value<'ast>) -> Self { match value { ast::Value::Integer(num) => types::Expression::from(num), @@ -163,15 +156,13 @@ impl<'ast, F: Field + PrimeField> From> for types::Expression From> for types::Expression { +impl<'ast> From> for types::Expression { fn from(expression: ast::NotExpression<'ast>) -> Self { types::Expression::Not(Box::new(types::Expression::from(*expression.expression))) } } -impl<'ast, F: Field + PrimeField> From> - for types::SpreadOrExpression -{ +impl<'ast> From> for types::SpreadOrExpression { fn from(s_or_e: ast::SpreadOrExpression<'ast>) -> Self { match s_or_e { ast::SpreadOrExpression::Spread(spread) => { @@ -184,7 +175,7 @@ impl<'ast, F: Field + PrimeField> From> } } -impl<'ast, F: Field + PrimeField> From> for types::Expression { +impl<'ast> From> for types::Expression { fn from(expression: ast::BinaryExpression<'ast>) -> Self { match expression.operation { // Boolean operations @@ -200,10 +191,10 @@ impl<'ast, F: Field + PrimeField> From> for types::E Box::new(types::Expression::from(*expression.left)), Box::new(types::Expression::from(*expression.right)), ), - ast::BinaryOperator::Neq => { + ast::BinaryOperator::Ne => { types::Expression::Not(Box::new(types::Expression::from(expression))) } - ast::BinaryOperator::Geq => types::Expression::Geq( + ast::BinaryOperator::Ge => types::Expression::Ge( Box::new(types::Expression::from(*expression.left)), Box::new(types::Expression::from(*expression.right)), ), @@ -211,7 +202,7 @@ impl<'ast, F: Field + PrimeField> From> for types::E Box::new(types::Expression::from(*expression.left)), Box::new(types::Expression::from(*expression.right)), ), - ast::BinaryOperator::Leq => types::Expression::Leq( + ast::BinaryOperator::Le => types::Expression::Le( Box::new(types::Expression::from(*expression.left)), Box::new(types::Expression::from(*expression.right)), ), @@ -244,7 +235,7 @@ impl<'ast, F: Field + PrimeField> From> for types::E } } -impl<'ast, F: Field + PrimeField> From> for types::Expression { +impl<'ast> From> for types::Expression { fn from(expression: ast::TernaryExpression<'ast>) -> Self { types::Expression::IfElse( Box::new(types::Expression::from(*expression.first)), @@ -254,7 +245,7 @@ impl<'ast, F: Field + PrimeField> From> for types:: } } -impl<'ast, F: Field + PrimeField> From> for types::Expression { +impl<'ast> From> for types::Expression { fn from(array: ast::ArrayInlineExpression<'ast>) -> Self { types::Expression::Array( array @@ -265,20 +256,16 @@ impl<'ast, F: Field + PrimeField> From> for typ ) } } -impl<'ast, F: Field + PrimeField> From> - for types::Expression -{ +impl<'ast> From> for types::Expression { fn from(array: ast::ArrayInitializerExpression<'ast>) -> Self { - let count = types::Expression::::get_count(array.count); + let count = types::Expression::get_count(array.count); let expression = Box::new(types::SpreadOrExpression::from(*array.expression)); types::Expression::Array(vec![expression; count]) } } -impl<'ast, F: Field + PrimeField> From> - for types::CircuitFieldDefinition -{ +impl<'ast> From> for types::CircuitFieldDefinition { fn from(member: ast::CircuitField<'ast>) -> Self { types::CircuitFieldDefinition { identifier: types::Identifier::from(member.identifier), @@ -287,22 +274,20 @@ impl<'ast, F: Field + PrimeField> From> } } -impl<'ast, F: Field + PrimeField> From> - for types::Expression -{ +impl<'ast> From> for types::Expression { fn from(expression: ast::CircuitInlineExpression<'ast>) -> Self { let variable = types::Identifier::from(expression.identifier); let members = expression .members .into_iter() .map(|member| types::CircuitFieldDefinition::from(member)) - .collect::>>(); + .collect::>(); types::Expression::Circuit(variable, members) } } -impl<'ast, F: Field + PrimeField> From> for types::Expression { +impl<'ast> From> for types::Expression { fn from(expression: ast::PostfixExpression<'ast>) -> Self { let variable = types::Expression::Identifier(types::Identifier::from(expression.identifier)); @@ -346,7 +331,7 @@ impl<'ast, F: Field + PrimeField> From> for types:: } } -impl<'ast, F: Field + PrimeField> From> for types::Expression { +impl<'ast> From> for types::Expression { fn from(expression: ast::Expression<'ast>) -> Self { match expression { ast::Expression::Value(value) => types::Expression::from(value), @@ -362,7 +347,7 @@ impl<'ast, F: Field + PrimeField> From> for types::Express } } -impl<'ast, F: Field + PrimeField> types::Expression { +impl<'ast> types::Expression { fn get_count(count: ast::Value<'ast>) -> usize { match count { ast::Value::Integer(integer) => integer @@ -381,7 +366,7 @@ impl<'ast, F: Field + PrimeField> types::Expression { } // ast::Assignee -> types::Expression for operator assign statements -impl<'ast, F: Field + PrimeField> From> for types::Expression { +impl<'ast> From> for types::Expression { fn from(assignee: ast::Assignee<'ast>) -> Self { let variable = types::Expression::Identifier(types::Identifier::from(assignee.identifier)); @@ -406,13 +391,13 @@ impl<'ast, F: Field + PrimeField> From> for types::Expressio /// pest ast -> types::Assignee -impl<'ast, F: Field + PrimeField> From> for types::Assignee { +impl<'ast> From> for types::Assignee { fn from(variable: ast::Identifier<'ast>) -> Self { types::Assignee::Identifier(types::Identifier::from(variable)) } } -impl<'ast, F: Field + PrimeField> From> for types::Assignee { +impl<'ast> From> for types::Assignee { fn from(assignee: ast::Assignee<'ast>) -> Self { let variable = types::Assignee::from(assignee.identifier); @@ -435,7 +420,7 @@ impl<'ast, F: Field + PrimeField> From> for types::Assignee< /// pest ast -> types::Statement -impl<'ast, F: Field + PrimeField> From> for types::Statement { +impl<'ast> From> for types::Statement { fn from(statement: ast::ReturnStatement<'ast>) -> Self { types::Statement::Return( statement @@ -447,7 +432,7 @@ impl<'ast, F: Field + PrimeField> From> for types::St } } -impl<'ast, F: Field + PrimeField> From> for types::Statement { +impl<'ast> From> for types::Statement { fn from(statement: ast::DefinitionStatement<'ast>) -> Self { types::Statement::Definition( types::Variable::from(statement.variable), @@ -456,7 +441,7 @@ impl<'ast, F: Field + PrimeField> From> for types } } -impl<'ast, F: Field + PrimeField> From> for types::Statement { +impl<'ast> From> for types::Statement { fn from(statement: ast::AssignStatement<'ast>) -> Self { match statement.assign { ast::OperationAssign::Assign(ref _assign) => types::Statement::Assign( @@ -512,9 +497,7 @@ impl<'ast, F: Field + PrimeField> From> for types::St } } -impl<'ast, F: Field + PrimeField> From> - for types::Statement -{ +impl<'ast> From> for types::Statement { fn from(statement: ast::MultipleAssignmentStatement<'ast>) -> Self { let variables = statement .variables @@ -536,9 +519,7 @@ impl<'ast, F: Field + PrimeField> From> } } -impl<'ast, F: Field + PrimeField> From> - for types::ConditionalNestedOrEnd -{ +impl<'ast> From> for types::ConditionalNestedOrEnd { fn from(statement: ast::ConditionalNestedOrEnd<'ast>) -> Self { match statement { ast::ConditionalNestedOrEnd::Nested(nested) => types::ConditionalNestedOrEnd::Nested( @@ -554,9 +535,7 @@ impl<'ast, F: Field + PrimeField> From> } } -impl<'ast, F: Field + PrimeField> From> - for types::ConditionalStatement -{ +impl<'ast> From> for types::ConditionalStatement { fn from(statement: ast::ConditionalStatement<'ast>) -> Self { types::ConditionalStatement { condition: types::Expression::from(statement.condition), @@ -573,14 +552,14 @@ impl<'ast, F: Field + PrimeField> From> } } -impl<'ast, F: Field + PrimeField> From> for types::Statement { +impl<'ast> From> for types::Statement { fn from(statement: ast::ForStatement<'ast>) -> Self { - let from = match types::Expression::::from(statement.start) { + let from = match types::Expression::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::::from(statement.stop) { + let to = match types::Expression::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), @@ -599,7 +578,7 @@ impl<'ast, F: Field + PrimeField> From> for types::State } } -impl<'ast, F: Field + PrimeField> From> for types::Statement { +impl<'ast> From> for types::Statement { fn from(statement: ast::AssertStatement<'ast>) -> Self { match statement { ast::AssertStatement::AssertEq(assert_eq) => types::Statement::AssertEq( @@ -610,13 +589,13 @@ impl<'ast, F: Field + PrimeField> From> for types::St } } -impl<'ast, F: Field + PrimeField> From> for types::Statement { +impl<'ast> From> for types::Statement { fn from(statement: ast::ExpressionStatement<'ast>) -> Self { types::Statement::Expression(types::Expression::from(statement.expression)) } } -impl<'ast, F: Field + PrimeField> From> for types::Statement { +impl<'ast> From> for types::Statement { fn from(statement: ast::Statement<'ast>) -> Self { match statement { ast::Statement::Return(statement) => types::Statement::from(statement), @@ -647,39 +626,39 @@ impl From for types::IntegerType { } } -impl From for types::Type { +impl From for types::Type { fn from(basic_type: ast::BasicType) -> Self { match basic_type { ast::BasicType::Integer(_type) => { types::Type::IntegerType(types::IntegerType::from(_type)) } - ast::BasicType::Field(_type) => types::Type::FieldElement, + ast::BasicType::Field(_type) => types::Type::Field, ast::BasicType::Group(_type) => types::Type::Group, ast::BasicType::Boolean(_type) => types::Type::Boolean, } } } -impl<'ast, F: Field + PrimeField> From> for types::Type { +impl<'ast> From> for types::Type { fn from(array_type: ast::ArrayType<'ast>) -> Self { let element_type = Box::new(types::Type::from(array_type._type)); let dimensions = array_type .dimensions .into_iter() - .map(|row| types::Expression::::get_count(row)) + .map(|row| types::Expression::get_count(row)) .collect(); types::Type::Array(element_type, dimensions) } } -impl<'ast, F: Field + PrimeField> From> for types::Type { +impl<'ast> From> for types::Type { fn from(circuit_type: ast::CircuitType<'ast>) -> Self { types::Type::Circuit(types::Identifier::from(circuit_type.identifier)) } } -impl<'ast, F: Field + PrimeField> From> for types::Type { +impl<'ast> From> for types::Type { fn from(_type: ast::Type<'ast>) -> Self { match _type { ast::Type::Basic(_type) => types::Type::from(_type), @@ -692,9 +671,7 @@ impl<'ast, F: Field + PrimeField> From> for types::Type { /// pest ast -> types::Circuit -impl<'ast, F: Field + PrimeField> From> - for types::CircuitMember -{ +impl<'ast> From> for types::CircuitMember { fn from(circuit_value: ast::CircuitFieldDefinition<'ast>) -> Self { types::CircuitMember::CircuitField( types::Identifier::from(circuit_value.identifier), @@ -703,7 +680,7 @@ impl<'ast, F: Field + PrimeField> From> } } -impl<'ast, F: Field + PrimeField> From> for types::CircuitMember { +impl<'ast> From> for types::CircuitMember { fn from(circuit_function: ast::CircuitFunction<'ast>) -> Self { types::CircuitMember::CircuitFunction( circuit_function._static.is_some(), @@ -712,7 +689,7 @@ impl<'ast, F: Field + PrimeField> From> for types::Ci } } -impl<'ast, F: Field + PrimeField> From> for types::CircuitMember { +impl<'ast> From> for types::CircuitMember { fn from(object: ast::CircuitMember<'ast>) -> Self { match object { ast::CircuitMember::CircuitFieldDefinition(circuit_value) => { @@ -725,7 +702,7 @@ impl<'ast, F: Field + PrimeField> From> for types::Circ } } -impl<'ast, F: Field + PrimeField> From> for types::Circuit { +impl<'ast> From> for types::Circuit { fn from(circuit: ast::Circuit<'ast>) -> Self { let variable = types::Identifier::from(circuit.identifier); let members = circuit @@ -743,7 +720,7 @@ impl<'ast, F: Field + PrimeField> From> for types::Circuit /// pest ast -> function types::Parameters -impl<'ast, F: Field + PrimeField> From> for types::InputModel { +impl<'ast> From> for types::InputModel { fn from(parameter: ast::InputModel<'ast>) -> Self { types::InputModel { identifier: types::Identifier::from(parameter.identifier), @@ -759,7 +736,7 @@ impl<'ast, F: Field + PrimeField> From> for types::InputMo /// pest ast -> types::Function -impl<'ast, F: Field + PrimeField> From> for types::Function { +impl<'ast> From> for types::Function { fn from(function_definition: ast::Function<'ast>) -> Self { let function_name = types::Identifier::from(function_definition.function_name); let parameters = function_definition @@ -789,7 +766,7 @@ impl<'ast, F: Field + PrimeField> From> for types::Function< /// pest ast -> Import -impl<'ast, F: Field + PrimeField> From> for ImportSymbol { +impl<'ast> From> for ImportSymbol { fn from(symbol: ast::ImportSymbol<'ast>) -> Self { ImportSymbol { symbol: types::Identifier::from(symbol.value), @@ -798,7 +775,7 @@ impl<'ast, F: Field + PrimeField> From> for ImportSymbol } } -impl<'ast, F: Field + PrimeField> From> for Import { +impl<'ast> From> for Import { fn from(import: ast::Import<'ast>) -> Self { Import { path_string: import.source.value, @@ -813,14 +790,14 @@ impl<'ast, F: Field + PrimeField> From> for Import { /// pest ast -> types::Program -impl<'ast, F: Field + PrimeField> types::Program { +impl<'ast> types::Program { pub fn from(file: ast::File<'ast>, name: String) -> Self { // Compiled ast -> aleo program representation let imports = file .imports .into_iter() .map(|import| Import::from(import)) - .collect::>>(); + .collect::>(); let mut circuits = HashMap::new(); let mut functions = HashMap::new(); diff --git a/compiler/tests/boolean/mod.rs b/compiler/tests/boolean/mod.rs index 8d0b093699..f271aae78b 100644 --- a/compiler/tests/boolean/mod.rs +++ b/compiler/tests/boolean/mod.rs @@ -8,22 +8,23 @@ use snarkos_models::gadgets::utilities::boolean::Boolean; const DIRECTORY_NAME: &str = "tests/boolean/"; -pub fn output_true(program: EdwardsTestCompiler) { +pub fn output_expected_boolean(program: EdwardsTestCompiler, boolean: bool) { let output = get_output(program); assert_eq!( - EdwardsConstrainedValue::Return(vec![ConstrainedValue::Boolean(Boolean::Constant(true))]) - .to_string(), + EdwardsConstrainedValue::Return(vec![ConstrainedValue::Boolean(Boolean::Constant( + boolean + ))]) + .to_string(), output.to_string() ); } +pub fn output_true(program: EdwardsTestCompiler) { + output_expected_boolean(program, true) +} + pub fn output_false(program: EdwardsTestCompiler) { - let output = get_output(program); - assert_eq!( - EdwardsConstrainedValue::Return(vec![ConstrainedValue::Boolean(Boolean::Constant(false))]) - .to_string(), - output.to_string() - ); + output_expected_boolean(program, false) } fn fail_evaluate(program: EdwardsTestCompiler) { diff --git a/compiler/tests/field/add.leo b/compiler/tests/field/add.leo new file mode 100644 index 0000000000..a99723ef76 --- /dev/null +++ b/compiler/tests/field/add.leo @@ -0,0 +1,3 @@ +function main(a: field, b: field) -> field { + return a + b +} \ No newline at end of file diff --git a/compiler/tests/field/assert_eq.leo b/compiler/tests/field/assert_eq.leo new file mode 100644 index 0000000000..ebf7eecb1a --- /dev/null +++ b/compiler/tests/field/assert_eq.leo @@ -0,0 +1,3 @@ +function main(a: field, b: field) { + assert_eq!(a, b); +} \ No newline at end of file diff --git a/compiler/tests/field/div.leo b/compiler/tests/field/div.leo new file mode 100644 index 0000000000..6214073024 --- /dev/null +++ b/compiler/tests/field/div.leo @@ -0,0 +1,3 @@ +function main(a: field, b: field) -> field { + return a / b +} \ No newline at end of file diff --git a/compiler/tests/field/eq.leo b/compiler/tests/field/eq.leo new file mode 100644 index 0000000000..839bf5d64e --- /dev/null +++ b/compiler/tests/field/eq.leo @@ -0,0 +1,3 @@ +function main(a: field, b: field) -> bool { + return a == b +} \ No newline at end of file diff --git a/compiler/tests/field/ge.leo b/compiler/tests/field/ge.leo new file mode 100644 index 0000000000..860cdfc695 --- /dev/null +++ b/compiler/tests/field/ge.leo @@ -0,0 +1,3 @@ +function main(a: field, b: field) -> bool { + return a >= b +} \ No newline at end of file diff --git a/compiler/tests/field/gt.leo b/compiler/tests/field/gt.leo new file mode 100644 index 0000000000..4bec88c764 --- /dev/null +++ b/compiler/tests/field/gt.leo @@ -0,0 +1,3 @@ +function main(a: field, b: field) -> bool { + return a > b +} \ No newline at end of file diff --git a/compiler/tests/field_element/input_field.leo b/compiler/tests/field/input.leo similarity index 100% rename from compiler/tests/field_element/input_field.leo rename to compiler/tests/field/input.leo diff --git a/compiler/tests/field/le.leo b/compiler/tests/field/le.leo new file mode 100644 index 0000000000..8cdc10e5c4 --- /dev/null +++ b/compiler/tests/field/le.leo @@ -0,0 +1,3 @@ +function main(a: field, b: field) -> bool { + return a <= b +} \ No newline at end of file diff --git a/compiler/tests/field/lt.leo b/compiler/tests/field/lt.leo new file mode 100644 index 0000000000..4e4acf457c --- /dev/null +++ b/compiler/tests/field/lt.leo @@ -0,0 +1,3 @@ +function main(a: field, b: field) -> bool { + return a < b +} \ No newline at end of file diff --git a/compiler/tests/field/mod.rs b/compiler/tests/field/mod.rs new file mode 100644 index 0000000000..a2e46f1398 --- /dev/null +++ b/compiler/tests/field/mod.rs @@ -0,0 +1,438 @@ +use crate::boolean::{output_expected_boolean, output_false, output_true}; +use crate::{compile_program, get_error, get_output, EdwardsConstrainedValue, EdwardsTestCompiler}; +use leo_compiler::{ + errors::{CompilerError, FieldError, FunctionError}, + ConstrainedValue, FieldType, InputValue, +}; + +use snarkos_curves::edwards_bls12::Fq; +use snarkos_gadgets::curves::edwards_bls12::FqGadget; +use snarkos_models::curves::{Field, PrimeField}; +use snarkos_models::gadgets::{ + curves::field::FieldGadget, + r1cs::{ConstraintSystem, TestConstraintSystem}, +}; +use snarkos_utilities::biginteger::BigInteger256; + +const DIRECTORY_NAME: &str = "tests/field/"; + +fn output_expected_constant(program: EdwardsTestCompiler, expected: Fq) { + let output = get_output(program); + assert_eq!( + EdwardsConstrainedValue::Return(vec![ConstrainedValue::Field(FieldType::Constant( + expected + ))]) + .to_string(), + output.to_string() + ); +} + +fn output_expected_allocated(program: EdwardsTestCompiler, expected: FqGadget) { + let output = get_output(program); + + match output { + EdwardsConstrainedValue::Return(vec) => match vec.as_slice() { + [ConstrainedValue::Field(FieldType::Allocated(fp_gadget))] => { + assert_eq!(*fp_gadget, expected as FqGadget) + } + _ => panic!("program output unknown return value"), + }, + _ => panic!("program output unknown return value"), + } +} + +fn output_zero(program: EdwardsTestCompiler) { + output_expected_constant(program, Fq::zero()) +} + +fn output_one(program: EdwardsTestCompiler) { + output_expected_constant(program, Fq::one()) +} + +fn fail_field(program: EdwardsTestCompiler) { + match get_error(program) { + CompilerError::FunctionError(FunctionError::FieldError(FieldError::Invalid(_string))) => {} + error => panic!("Expected invalid field error, got {}", error), + } +} + +fn fail_synthesis(program: EdwardsTestCompiler) { + match get_error(program) { + CompilerError::FunctionError(FunctionError::FieldError(FieldError::SynthesisError( + _string, + ))) => {} + error => panic!("Expected synthesis error, got {}", error), + } +} + +#[test] +fn test_zero() { + let program = compile_program(DIRECTORY_NAME, "zero.leo").unwrap(); + output_zero(program); +} + +#[test] +fn test_one() { + let program = compile_program(DIRECTORY_NAME, "one.leo").unwrap(); + output_one(program); +} + +#[test] +fn test_input_pass() { + let mut program = compile_program(DIRECTORY_NAME, "input.leo").unwrap(); + program.set_inputs(vec![Some(InputValue::Field("1".into()))]); + + let cs = TestConstraintSystem::::new(); + let expected = FqGadget::one(cs).unwrap(); + + output_expected_allocated(program, expected) +} + +#[test] +fn test_input_fail_bool() { + let mut program = compile_program(DIRECTORY_NAME, "input.leo").unwrap(); + program.set_inputs(vec![Some(InputValue::Boolean(true))]); + fail_field(program); +} + +#[test] +fn test_input_fail_none() { + let mut program = compile_program(DIRECTORY_NAME, "input.leo").unwrap(); + program.set_inputs(vec![None]); + fail_synthesis(program); +} + +#[test] +fn test_add() { + use std::ops::Add; + + for _ in 0..10 { + let r1: u64 = rand::random(); + let r2: u64 = rand::random(); + + let b1 = BigInteger256::from(r1); + let b2 = BigInteger256::from(r2); + + let f1: Fq = Fq::from_repr(b1); + let f2: Fq = Fq::from_repr(b2); + + let sum = f1.add(&f2); + + let cs = TestConstraintSystem::::new(); + let sum_allocated = FqGadget::from(cs, &sum); + + let mut program = compile_program(DIRECTORY_NAME, "add.leo").unwrap(); + program.set_inputs(vec![ + Some(InputValue::Field(r1.to_string())), + Some(InputValue::Field(r2.to_string())), + ]); + + output_expected_allocated(program, sum_allocated); + } +} + +#[test] +fn test_sub() { + use std::ops::Sub; + + for _ in 0..10 { + let r1: u64 = rand::random(); + let r2: u64 = rand::random(); + + let b1 = BigInteger256::from(r1); + let b2 = BigInteger256::from(r2); + + let f1: Fq = Fq::from_repr(b1); + let f2: Fq = Fq::from_repr(b2); + + let difference = f1.sub(&f2); + + let cs = TestConstraintSystem::::new(); + let difference_allocated = FqGadget::from(cs, &difference); + + let mut program = compile_program(DIRECTORY_NAME, "sub.leo").unwrap(); + program.set_inputs(vec![ + Some(InputValue::Field(r1.to_string())), + Some(InputValue::Field(r2.to_string())), + ]); + + output_expected_allocated(program, difference_allocated); + } +} + +#[test] +fn test_mul() { + use std::ops::Mul; + + for _ in 0..10 { + let r1: u64 = rand::random(); + let r2: u64 = rand::random(); + + let b1 = BigInteger256::from(r1); + let b2 = BigInteger256::from(r2); + + let f1: Fq = Fq::from_repr(b1); + let f2: Fq = Fq::from_repr(b2); + + let product = f1.mul(&f2); + + let cs = TestConstraintSystem::::new(); + let product_allocated = FqGadget::from(cs, &product); + + let mut program = compile_program(DIRECTORY_NAME, "mul.leo").unwrap(); + program.set_inputs(vec![ + Some(InputValue::Field(r1.to_string())), + Some(InputValue::Field(r2.to_string())), + ]); + + output_expected_allocated(program, product_allocated); + } +} + +#[test] +fn test_div() { + use std::ops::Div; + + for _ in 0..10 { + let r1: u64 = rand::random(); + let r2: u64 = rand::random(); + + let b1 = BigInteger256::from(r1); + let b2 = BigInteger256::from(r2); + + let f1: Fq = Fq::from_repr(b1); + let f2: Fq = Fq::from_repr(b2); + + let quotient = f1.div(&f2); + + let cs = TestConstraintSystem::::new(); + let quotient_allocated = FqGadget::from(cs, "ient); + + let mut program = compile_program(DIRECTORY_NAME, "div.leo").unwrap(); + program.set_inputs(vec![ + Some(InputValue::Field(r1.to_string())), + Some(InputValue::Field(r2.to_string())), + ]); + + output_expected_allocated(program, quotient_allocated); + } +} + +#[test] +fn test_eq() { + for _ in 0..10 { + let r1: u64 = rand::random(); + + // test equal + let mut program = compile_program(DIRECTORY_NAME, "eq.leo").unwrap(); + program.set_inputs(vec![ + Some(InputValue::Field(r1.to_string())), + Some(InputValue::Field(r1.to_string())), + ]); + + output_true(program); + + // test not equal + let r2: u64 = rand::random(); + + let result = r1.eq(&r2); + + let mut program = compile_program(DIRECTORY_NAME, "eq.leo").unwrap(); + program.set_inputs(vec![ + Some(InputValue::Field(r1.to_string())), + Some(InputValue::Field(r2.to_string())), + ]); + + output_expected_boolean(program, result) + } +} + +#[test] +fn test_ge() { + for _ in 0..10 { + let r1: u64 = rand::random(); + + // test equal + let mut program = compile_program(DIRECTORY_NAME, "ge.leo").unwrap(); + program.set_inputs(vec![ + Some(InputValue::Field(r1.to_string())), + Some(InputValue::Field(r1.to_string())), + ]); + + output_true(program); + + // test greater than + let r2: u64 = rand::random(); + + let result = r1.ge(&r2); + + let mut program = compile_program(DIRECTORY_NAME, "ge.leo").unwrap(); + program.set_inputs(vec![ + Some(InputValue::Field(r1.to_string())), + Some(InputValue::Field(r2.to_string())), + ]); + + output_expected_boolean(program, result) + } +} + +#[test] +fn test_gt() { + for _ in 0..10 { + let r1: u64 = rand::random(); + + // test equal + let mut program = compile_program(DIRECTORY_NAME, "gt.leo").unwrap(); + program.set_inputs(vec![ + Some(InputValue::Field(r1.to_string())), + Some(InputValue::Field(r1.to_string())), + ]); + + output_false(program); + + // test greater than + let r2: u64 = rand::random(); + + let result = r1.gt(&r2); + + let mut program = compile_program(DIRECTORY_NAME, "gt.leo").unwrap(); + program.set_inputs(vec![ + Some(InputValue::Field(r1.to_string())), + Some(InputValue::Field(r2.to_string())), + ]); + + output_expected_boolean(program, result) + } +} + +#[test] +fn test_le() { + for _ in 0..10 { + let r1: u64 = rand::random(); + + // test equal + let mut program = compile_program(DIRECTORY_NAME, "le.leo").unwrap(); + program.set_inputs(vec![ + Some(InputValue::Field(r1.to_string())), + Some(InputValue::Field(r1.to_string())), + ]); + + output_true(program); + + // test greater than + let r2: u64 = rand::random(); + + let result = r1.le(&r2); + + let mut program = compile_program(DIRECTORY_NAME, "le.leo").unwrap(); + program.set_inputs(vec![ + Some(InputValue::Field(r1.to_string())), + Some(InputValue::Field(r2.to_string())), + ]); + + output_expected_boolean(program, result) + } +} + +#[test] +fn test_lt() { + for _ in 0..10 { + let r1: u64 = rand::random(); + + // test equal + let mut program = compile_program(DIRECTORY_NAME, "lt.leo").unwrap(); + program.set_inputs(vec![ + Some(InputValue::Field(r1.to_string())), + Some(InputValue::Field(r1.to_string())), + ]); + + output_false(program); + + // test greater than + let r2: u64 = rand::random(); + + let result = r1.lt(&r2); + + let mut program = compile_program(DIRECTORY_NAME, "lt.leo").unwrap(); + program.set_inputs(vec![ + Some(InputValue::Field(r1.to_string())), + Some(InputValue::Field(r2.to_string())), + ]); + + output_expected_boolean(program, result) + } +} + +#[test] +fn test_assert_eq_pass() { + for _ in 0..10 { + let r1: u64 = rand::random(); + + let mut program = compile_program(DIRECTORY_NAME, "assert_eq.leo").unwrap(); + program.set_inputs(vec![ + Some(InputValue::Field(r1.to_string())), + Some(InputValue::Field(r1.to_string())), + ]); + + let _ = get_output(program); + } +} + +#[test] +fn test_assert_eq_fail() { + for _ in 0..10 { + let r1: u64 = rand::random(); + let r2: u64 = rand::random(); + + if r1 == r2 { + continue; + } + + let mut program = compile_program(DIRECTORY_NAME, "assert_eq.leo").unwrap(); + program.set_inputs(vec![ + Some(InputValue::Field(r1.to_string())), + Some(InputValue::Field(r2.to_string())), + ]); + + let mut cs = TestConstraintSystem::::new(); + let _ = program.compile_constraints(&mut cs).unwrap(); + assert!(!cs.is_satisfied()); + } +} + +#[test] +fn test_ternary() { + let r1: u64 = rand::random(); + let r2: u64 = rand::random(); + + let b1 = BigInteger256::from(r1); + let b2 = BigInteger256::from(r2); + + let f1: Fq = Fq::from_repr(b1); + let f2: Fq = Fq::from_repr(b2); + + let mut cs = TestConstraintSystem::::new(); + let g1 = FqGadget::from(cs.ns(|| "g1"), &f1); + let g2 = FqGadget::from(cs.ns(|| "g2"), &f2); + + let mut program_1 = compile_program(DIRECTORY_NAME, "ternary.leo").unwrap(); + let mut program_2 = program_1.clone(); + + // true -> field 1 + program_1.set_inputs(vec![ + Some(InputValue::Boolean(true)), + Some(InputValue::Field(r1.to_string())), + Some(InputValue::Field(r2.to_string())), + ]); + + output_expected_allocated(program_1, g1); + + // false -> field 2 + program_2.set_inputs(vec![ + Some(InputValue::Boolean(false)), + Some(InputValue::Field(r1.to_string())), + Some(InputValue::Field(r2.to_string())), + ]); + + output_expected_allocated(program_2, g2); +} diff --git a/compiler/tests/field/mul.leo b/compiler/tests/field/mul.leo new file mode 100644 index 0000000000..8223cc8bc8 --- /dev/null +++ b/compiler/tests/field/mul.leo @@ -0,0 +1,3 @@ +function main(a: field, b: field) -> field { + return a * b +} \ No newline at end of file diff --git a/compiler/tests/field_element/one.leo b/compiler/tests/field/one.leo similarity index 100% rename from compiler/tests/field_element/one.leo rename to compiler/tests/field/one.leo diff --git a/compiler/tests/field/sub.leo b/compiler/tests/field/sub.leo new file mode 100644 index 0000000000..7c49a87da8 --- /dev/null +++ b/compiler/tests/field/sub.leo @@ -0,0 +1,3 @@ +function main(a: field, b: field) -> field { + return a - b +} \ No newline at end of file diff --git a/compiler/tests/field/ternary.leo b/compiler/tests/field/ternary.leo new file mode 100644 index 0000000000..c85ac6a764 --- /dev/null +++ b/compiler/tests/field/ternary.leo @@ -0,0 +1,3 @@ +function main(b: bool, f1: field, f2: field) -> field { + return if b ? f1 : f2 +} \ No newline at end of file diff --git a/compiler/tests/field_element/zero.leo b/compiler/tests/field/zero.leo similarity index 100% rename from compiler/tests/field_element/zero.leo rename to compiler/tests/field/zero.leo diff --git a/compiler/tests/field_element/mod.rs b/compiler/tests/field_element/mod.rs deleted file mode 100644 index 9987d7ca2f..0000000000 --- a/compiler/tests/field_element/mod.rs +++ /dev/null @@ -1,76 +0,0 @@ -use crate::{compile_program, get_error, get_output, EdwardsConstrainedValue, EdwardsTestCompiler}; -use leo_compiler::{ - errors::{CompilerError, FieldElementError, FunctionError}, - ConstrainedValue, FieldElement, InputValue, -}; - -use snarkos_curves::edwards_bls12::Fq; -use snarkos_models::curves::Field; - -const DIRECTORY_NAME: &str = "tests/field_element/"; - -fn output_zero(program: EdwardsTestCompiler) { - let output = get_output(program); - assert_eq!( - EdwardsConstrainedValue::Return(vec![ConstrainedValue::FieldElement( - FieldElement::Constant(Fq::zero()) - )]) - .to_string(), - output.to_string() - ); -} - -fn output_one(program: EdwardsTestCompiler) { - let output = get_output(program); - assert_eq!( - EdwardsConstrainedValue::Return(vec![ConstrainedValue::FieldElement( - FieldElement::Constant(Fq::one()) - )]) - .to_string(), - output.to_string() - ); -} - -fn fail_field(program: EdwardsTestCompiler) { - match get_error(program) { - CompilerError::FunctionError(FunctionError::FieldElementError( - FieldElementError::InvalidField(_string), - )) => {} - error => panic!("Expected invalid field error, got {}", error), - } -} - -fn fail_synthesis(program: EdwardsTestCompiler) { - match get_error(program) { - CompilerError::FunctionError(FunctionError::FieldElementError( - FieldElementError::SynthesisError(_string), - )) => {} - error => panic!("Expected synthesis error, got {}", error), - } -} - -#[test] -fn test_zero() { - let program = compile_program(DIRECTORY_NAME, "zero.leo").unwrap(); - output_zero(program); -} - -#[test] -fn test_one() { - let program = compile_program(DIRECTORY_NAME, "one.leo").unwrap(); - output_one(program); -} - -#[test] -fn test_input_field_bool() { - let mut program = compile_program(DIRECTORY_NAME, "input_field.leo").unwrap(); - program.set_inputs(vec![Some(InputValue::Boolean(true))]); - fail_field(program); -} - -#[test] -fn test_input_field_none() { - let mut program = compile_program(DIRECTORY_NAME, "input_field.leo").unwrap(); - program.set_inputs(vec![None]); - fail_synthesis(program); -} diff --git a/compiler/tests/group/mod.rs b/compiler/tests/group/mod.rs index 923fd1bf67..02d8effda8 100644 --- a/compiler/tests/group/mod.rs +++ b/compiler/tests/group/mod.rs @@ -1,18 +1,14 @@ use crate::{ boolean::{output_false, output_true}, - compile_program, get_error, get_output, EdwardsConstrainedValue, EdwardsTestCompiler, -}; -use leo_compiler::{ - errors::{CompilerError, FunctionError, StatementError}, - group::edwards_bls12::EdwardsGroupType, - ConstrainedValue, InputValue, + compile_program, fail_enforce, get_output, EdwardsConstrainedValue, EdwardsTestCompiler, }; +use leo_compiler::{group::edwards_bls12::EdwardsGroupType, ConstrainedValue, InputValue}; use snarkos_curves::edwards_bls12::{EdwardsAffine, Fq}; use snarkos_gadgets::curves::edwards_bls12::EdwardsBlsGadget; use snarkos_models::{ curves::Group, - gadgets::{curves::GroupGadget, r1cs::TestConstraintSystem, utilities::alloc::AllocGadget}, + gadgets::{r1cs::TestConstraintSystem, utilities::alloc::AllocGadget}, }; use std::str::FromStr; @@ -34,28 +30,22 @@ fn output_expected_constant(program: EdwardsTestCompiler, expected: EdwardsAffin fn output_expected_allocated(program: EdwardsTestCompiler, expected: EdwardsBlsGadget) { let output = get_output(program); - assert_eq!( - EdwardsConstrainedValue::Return(vec![ConstrainedValue::Group( - EdwardsGroupType::Allocated(expected) - )]) - .to_string(), - output.to_string() - ) + + match output { + EdwardsConstrainedValue::Return(vec) => match vec.as_slice() { + [ConstrainedValue::Group(EdwardsGroupType::Allocated(gadget))] => { + assert_eq!(*gadget, expected as EdwardsBlsGadget) + } + _ => panic!("program output unknown return value"), + }, + _ => panic!("program output unknown return value"), + } } fn output_zero(program: EdwardsTestCompiler) { output_expected_constant(program, EdwardsAffine::zero()) } -fn fail_enforce(program: EdwardsTestCompiler) { - match get_error(program) { - CompilerError::FunctionError(FunctionError::StatementError( - StatementError::SynthesisError(_), - )) => {} - error => panic!("Expected evaluate error, got {}", error), - } -} - #[test] fn test_zero() { let program = compile_program(DIRECTORY_NAME, "zero.leo").unwrap(); @@ -69,6 +59,20 @@ fn test_point() { output_expected_constant(program, point); } +#[test] +fn test_input() { + let mut program = compile_program(DIRECTORY_NAME, "input.leo").unwrap(); + program.set_inputs(vec![Some(InputValue::Group(TEST_POINT_1.into()))]); + + let mut cs = TestConstraintSystem::::new(); + let constant_point = EdwardsAffine::from_str(TEST_POINT_1).unwrap(); + let allocated_point = + >::alloc(&mut cs, || Ok(constant_point)) + .unwrap(); + + output_expected_allocated(program, allocated_point); +} + #[test] fn test_add() { use std::ops::Add; @@ -108,31 +112,17 @@ fn test_eq_false() { } #[test] -fn test_assert_eq_true() { +fn test_assert_eq_pass() { let program = compile_program(DIRECTORY_NAME, "assert_eq_true.leo").unwrap(); let _res = get_output(program); } #[test] -fn test_assert_eq_false() { +fn test_assert_eq_fail() { let program = compile_program(DIRECTORY_NAME, "assert_eq_false.leo").unwrap(); fail_enforce(program); } -#[test] -fn test_input() { - let mut program = compile_program(DIRECTORY_NAME, "input.leo").unwrap(); - program.set_inputs(vec![Some(InputValue::Group(TEST_POINT_1.into()))]); - - let mut cs = TestConstraintSystem::::new(); - let constant_point = EdwardsAffine::from_str(TEST_POINT_1).unwrap(); - let allocated_point = - >::alloc(&mut cs, || Ok(constant_point)) - .unwrap(); - - output_expected_allocated(program, allocated_point); -} - #[test] fn test_ternary() { let mut program_1 = compile_program(DIRECTORY_NAME, "ternary.leo").unwrap(); @@ -141,36 +131,20 @@ fn test_ternary() { // true -> point_1 program_1.set_inputs(vec![Some(InputValue::Boolean(true))]); + let mut cs = TestConstraintSystem::::new(); let point_1 = EdwardsAffine::from_str(TEST_POINT_1).unwrap(); - let output_1 = get_output(program_1); - let actual_1: EdwardsAffine = match output_1 { - EdwardsConstrainedValue::Return(vec) => match vec.as_slice() { - [ConstrainedValue::Group(EdwardsGroupType::Allocated(edwards_gadget))] => { - >::get_value(edwards_gadget) - .unwrap() - } - _ => panic!("program output unknown return value"), - }, - _ => panic!("program output unknown return value"), - }; - - assert_eq!(point_1, actual_1); + let expected_point_1 = + >::alloc(&mut cs, || Ok(point_1)) + .unwrap(); + output_expected_allocated(program_1, expected_point_1); // false -> point_2 program_2.set_inputs(vec![Some(InputValue::Boolean(false))]); + let mut cs = TestConstraintSystem::::new(); let point_2 = EdwardsAffine::from_str(TEST_POINT_2).unwrap(); - let output_2 = get_output(program_2); - let actual_2: EdwardsAffine = match output_2 { - EdwardsConstrainedValue::Return(vec) => match vec.as_slice() { - [ConstrainedValue::Group(EdwardsGroupType::Allocated(edwards_gadget))] => { - >::get_value(edwards_gadget) - .unwrap() - } - _ => panic!("program output unknown return value"), - }, - _ => panic!("program output unknown return value"), - }; - - assert_eq!(point_2, actual_2); + let expected_point_2 = + >::alloc(&mut cs, || Ok(point_2)) + .unwrap(); + output_expected_allocated(program_2, expected_point_2); } diff --git a/compiler/tests/mod.rs b/compiler/tests/mod.rs index 4220b21ce0..66c167a10b 100644 --- a/compiler/tests/mod.rs +++ b/compiler/tests/mod.rs @@ -1,7 +1,7 @@ pub mod array; pub mod boolean; pub mod circuit; -pub mod field_element; +pub mod field; pub mod function; pub mod group; pub mod import; @@ -10,7 +10,9 @@ pub mod mutability; pub mod statement; use leo_compiler::{ - compiler::Compiler, errors::CompilerError, group::edwards_bls12::EdwardsGroupType, + compiler::Compiler, + errors::{CompilerError, FunctionError, StatementError}, + group::edwards_bls12::EdwardsGroupType, ConstrainedValue, }; @@ -33,6 +35,15 @@ pub(crate) fn get_error(program: EdwardsTestCompiler) -> CompilerError { program.compile_constraints(&mut cs).unwrap_err() } +pub(crate) fn fail_enforce(program: EdwardsTestCompiler) { + match get_error(program) { + CompilerError::FunctionError(FunctionError::StatementError( + StatementError::SynthesisError(_), + )) => {} + error => panic!("Expected evaluate error, got {}", error), + } +} + pub(crate) fn compile_program( directory_name: &str, file_name: &str, diff --git a/leo/commands/build.rs b/leo/commands/build.rs index b3af1b5ae6..d61c3ab7aa 100644 --- a/leo/commands/build.rs +++ b/leo/commands/build.rs @@ -2,16 +2,10 @@ use crate::directories::{source::SOURCE_DIRECTORY_NAME, OutputsDirectory}; use crate::errors::{BuildError, CLIError}; use crate::files::{ChecksumFile, MainFile, Manifest, MAIN_FILE_NAME}; use crate::{cli::*, cli_types::*}; -use leo_compiler::{ - compiler::Compiler, - group::edwards_bls12::EdwardsGroupType -}; +use leo_compiler::{compiler::Compiler, group::edwards_bls12::EdwardsGroupType}; use snarkos_algorithms::snark::KeypairAssembly; -use snarkos_curves::{ - bls12_377::Bls12_377, - edwards_bls12::Fq -}; +use snarkos_curves::{bls12_377::Bls12_377, edwards_bls12::Fq}; use clap::ArgMatches; use std::convert::TryFrom; diff --git a/leo/commands/setup.rs b/leo/commands/setup.rs index 41ac26d4f4..7dd62e450e 100644 --- a/leo/commands/setup.rs +++ b/leo/commands/setup.rs @@ -2,18 +2,12 @@ use crate::commands::BuildCommand; use crate::errors::{CLIError, VerificationKeyFileError}; use crate::files::{Manifest, ProvingKeyFile, VerificationKeyFile}; use crate::{cli::*, cli_types::*}; -use leo_compiler::{ - compiler::Compiler, - group::edwards_bls12::EdwardsGroupType -}; +use leo_compiler::{compiler::Compiler, group::edwards_bls12::EdwardsGroupType}; use snarkos_algorithms::snark::{ generate_random_parameters, prepare_verifying_key, Parameters, PreparedVerifyingKey, }; -use snarkos_curves::{ - bls12_377::Bls12_377, - edwards_bls12::Fq -}; +use snarkos_curves::{bls12_377::Bls12_377, edwards_bls12::Fq}; use snarkos_utilities::bytes::ToBytes; use clap::ArgMatches;