mirror of
https://github.com/AleoHQ/leo.git
synced 2024-12-01 18:56:38 +03:00
refactor constraints module
This commit is contained in:
parent
16d27629f9
commit
9bf0dc9a4a
@ -1,4 +1,4 @@
|
||||
def main() -> (fe[2u32]):
|
||||
// initialize an integer array with field values
|
||||
// initialize an field array with field values
|
||||
fe[2u32] a = [1fe, 2fe]
|
||||
return a
|
File diff suppressed because it is too large
Load Diff
188
src/aleo_program/constraints/boolean.rs
Normal file
188
src/aleo_program/constraints/boolean.rs
Normal file
@ -0,0 +1,188 @@
|
||||
use crate::aleo_program::constraints::{new_scope_from_variable, ResolvedProgram, ResolvedValue};
|
||||
use crate::aleo_program::{BooleanExpression, BooleanSpreadOrExpression, Variable};
|
||||
|
||||
use snarkos_models::curves::{Field, PrimeField};
|
||||
use snarkos_models::gadgets::{
|
||||
r1cs::ConstraintSystem,
|
||||
utilities::{alloc::AllocGadget, boolean::Boolean, eq::EqGadget},
|
||||
};
|
||||
|
||||
impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ResolvedProgram<F, CS> {
|
||||
pub(crate) fn bool_from_variable(
|
||||
&mut self,
|
||||
cs: &mut CS,
|
||||
scope: String,
|
||||
variable: Variable<F>,
|
||||
) -> Boolean {
|
||||
// Evaluate variable name in current function scope
|
||||
let variable_name = new_scope_from_variable(scope, &variable);
|
||||
|
||||
if self.contains_name(&variable_name) {
|
||||
// TODO: return synthesis error: "assignment missing" here
|
||||
match self.get(&variable_name).unwrap() {
|
||||
ResolvedValue::Boolean(boolean) => boolean.clone(),
|
||||
_ => panic!("expected a boolean, got field"),
|
||||
}
|
||||
} else {
|
||||
let argument = std::env::args()
|
||||
.nth(1)
|
||||
.unwrap_or("true".into())
|
||||
.parse::<bool>()
|
||||
.unwrap();
|
||||
println!(" argument passed to command line a = {:?}\n", argument);
|
||||
// let a = true;
|
||||
Boolean::alloc(cs.ns(|| variable.name), || Ok(argument)).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
fn get_bool_value(
|
||||
&mut self,
|
||||
cs: &mut CS,
|
||||
scope: String,
|
||||
expression: BooleanExpression<F>,
|
||||
) -> Boolean {
|
||||
match expression {
|
||||
BooleanExpression::Variable(variable) => self.bool_from_variable(cs, scope, variable),
|
||||
BooleanExpression::Value(value) => Boolean::Constant(value),
|
||||
expression => match self.enforce_boolean_expression(cs, scope, expression) {
|
||||
ResolvedValue::Boolean(value) => value,
|
||||
_ => unimplemented!("boolean expression did not resolve to boolean"),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn enforce_not(
|
||||
&mut self,
|
||||
cs: &mut CS,
|
||||
scope: String,
|
||||
expression: BooleanExpression<F>,
|
||||
) -> Boolean {
|
||||
let expression = self.get_bool_value(cs, scope, expression);
|
||||
|
||||
expression.not()
|
||||
}
|
||||
|
||||
fn enforce_or(
|
||||
&mut self,
|
||||
cs: &mut CS,
|
||||
scope: String,
|
||||
left: BooleanExpression<F>,
|
||||
right: BooleanExpression<F>,
|
||||
) -> Boolean {
|
||||
let left = self.get_bool_value(cs, scope.clone(), left);
|
||||
let right = self.get_bool_value(cs, scope.clone(), right);
|
||||
|
||||
Boolean::or(cs, &left, &right).unwrap()
|
||||
}
|
||||
|
||||
fn enforce_and(
|
||||
&mut self,
|
||||
cs: &mut CS,
|
||||
scope: String,
|
||||
left: BooleanExpression<F>,
|
||||
right: BooleanExpression<F>,
|
||||
) -> Boolean {
|
||||
let left = self.get_bool_value(cs, scope.clone(), left);
|
||||
let right = self.get_bool_value(cs, scope.clone(), right);
|
||||
|
||||
Boolean::and(cs, &left, &right).unwrap()
|
||||
}
|
||||
|
||||
fn enforce_bool_equality(
|
||||
&mut self,
|
||||
cs: &mut CS,
|
||||
scope: String,
|
||||
left: BooleanExpression<F>,
|
||||
right: BooleanExpression<F>,
|
||||
) -> Boolean {
|
||||
let left = self.get_bool_value(cs, scope.clone(), left);
|
||||
let right = self.get_bool_value(cs, scope.clone(), right);
|
||||
|
||||
left.enforce_equal(cs.ns(|| format!("enforce bool equal")), &right)
|
||||
.unwrap();
|
||||
|
||||
Boolean::Constant(true)
|
||||
}
|
||||
|
||||
pub(crate) fn enforce_boolean_expression(
|
||||
&mut self,
|
||||
cs: &mut CS,
|
||||
scope: String,
|
||||
expression: BooleanExpression<F>,
|
||||
) -> ResolvedValue<F> {
|
||||
match expression {
|
||||
BooleanExpression::Variable(variable) => {
|
||||
ResolvedValue::Boolean(self.bool_from_variable(cs, scope, variable))
|
||||
}
|
||||
BooleanExpression::Value(value) => ResolvedValue::Boolean(Boolean::Constant(value)),
|
||||
BooleanExpression::Not(expression) => {
|
||||
ResolvedValue::Boolean(self.enforce_not(cs, scope, *expression))
|
||||
}
|
||||
BooleanExpression::Or(left, right) => {
|
||||
ResolvedValue::Boolean(self.enforce_or(cs, scope, *left, *right))
|
||||
}
|
||||
BooleanExpression::And(left, right) => {
|
||||
ResolvedValue::Boolean(self.enforce_and(cs, scope, *left, *right))
|
||||
}
|
||||
BooleanExpression::IntegerEq(left, right) => {
|
||||
ResolvedValue::Boolean(self.enforce_integer_equality(cs, scope, *left, *right))
|
||||
}
|
||||
BooleanExpression::FieldEq(left, right) => {
|
||||
ResolvedValue::Boolean(self.enforce_field_equality(cs, scope, *left, *right))
|
||||
}
|
||||
BooleanExpression::BoolEq(left, right) => {
|
||||
ResolvedValue::Boolean(self.enforce_bool_equality(cs, scope, *left, *right))
|
||||
}
|
||||
BooleanExpression::IfElse(first, second, third) => {
|
||||
let resolved_first =
|
||||
match self.enforce_boolean_expression(cs, scope.clone(), *first) {
|
||||
ResolvedValue::Boolean(resolved) => resolved,
|
||||
_ => unimplemented!("if else conditional must resolve to boolean"),
|
||||
};
|
||||
if resolved_first.eq(&Boolean::Constant(true)) {
|
||||
self.enforce_boolean_expression(cs, scope, *second)
|
||||
} else {
|
||||
self.enforce_boolean_expression(cs, scope, *third)
|
||||
}
|
||||
}
|
||||
BooleanExpression::Array(array) => {
|
||||
let mut result = vec![];
|
||||
array.into_iter().for_each(|element| match *element {
|
||||
BooleanSpreadOrExpression::Spread(spread) => match spread {
|
||||
BooleanExpression::Variable(variable) => {
|
||||
let array_name = new_scope_from_variable(scope.clone(), &variable);
|
||||
match self.get(&array_name) {
|
||||
Some(value) => match value {
|
||||
ResolvedValue::BooleanArray(array) => {
|
||||
result.extend(array.clone())
|
||||
}
|
||||
value => unimplemented!(
|
||||
"spreads only implemented for arrays, got {}",
|
||||
value
|
||||
),
|
||||
},
|
||||
None => unimplemented!(
|
||||
"cannot copy elements from array that does not exist {}",
|
||||
variable.name
|
||||
),
|
||||
}
|
||||
}
|
||||
value => {
|
||||
unimplemented!("spreads only implemented for arrays, got {}", value)
|
||||
}
|
||||
},
|
||||
BooleanSpreadOrExpression::Expression(expression) => {
|
||||
match self.enforce_boolean_expression(cs, scope.clone(), expression) {
|
||||
ResolvedValue::Boolean(value) => result.push(value),
|
||||
value => {
|
||||
unimplemented!("expected boolean for boolean array, got {}", value)
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
ResolvedValue::BooleanArray(result)
|
||||
}
|
||||
expression => unimplemented!("boolean expression {}", expression),
|
||||
}
|
||||
}
|
||||
}
|
172
src/aleo_program/constraints/constraints.rs
Normal file
172
src/aleo_program/constraints/constraints.rs
Normal file
@ -0,0 +1,172 @@
|
||||
use crate::aleo_program::constraints::{new_scope_from_variable, ResolvedProgram, ResolvedValue};
|
||||
use crate::aleo_program::{Expression, Function, Import, Program, Statement, Type};
|
||||
use crate::ast;
|
||||
|
||||
use from_pest::FromPest;
|
||||
use snarkos_models::curves::{Field, PrimeField};
|
||||
use snarkos_models::gadgets::r1cs::ConstraintSystem;
|
||||
use std::fs;
|
||||
|
||||
impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ResolvedProgram<F, CS> {
|
||||
pub(crate) fn enforce_function(
|
||||
&mut self,
|
||||
cs: &mut CS,
|
||||
function: Function<F>,
|
||||
arguments: Vec<Expression<F>>,
|
||||
) -> ResolvedValue<F> {
|
||||
// Make sure we are given the correct number of arguments
|
||||
if function.parameters.len() != arguments.len() {
|
||||
unimplemented!(
|
||||
"function expected {} arguments, got {}",
|
||||
function.parameters.len(),
|
||||
arguments.len()
|
||||
)
|
||||
}
|
||||
|
||||
// Store arguments as variables in resolved program
|
||||
function
|
||||
.parameters
|
||||
.clone()
|
||||
.iter()
|
||||
.zip(arguments.clone().into_iter())
|
||||
.for_each(|(parameter, argument)| {
|
||||
// Check visibility here
|
||||
|
||||
// Check that argument is correct type
|
||||
match parameter.ty.clone() {
|
||||
Type::U32 => {
|
||||
match self.enforce_expression(cs, function.get_name(), argument) {
|
||||
ResolvedValue::U32(number) => {
|
||||
// Store argument as variable with {function_name}_{parameter name}
|
||||
let variable_name = new_scope_from_variable(
|
||||
function.get_name(),
|
||||
¶meter.variable,
|
||||
);
|
||||
self.store(variable_name, ResolvedValue::U32(number));
|
||||
}
|
||||
argument => unimplemented!("expected field argument got {}", argument),
|
||||
}
|
||||
}
|
||||
Type::FieldElement => {
|
||||
match self.enforce_expression(cs, function.get_name(), argument) {
|
||||
ResolvedValue::FieldElement(field) => {
|
||||
// Store argument as variable with {function_name}_{parameter name}
|
||||
let variable_name = new_scope_from_variable(
|
||||
function.get_name(),
|
||||
¶meter.variable,
|
||||
);
|
||||
self.store(variable_name, ResolvedValue::FieldElement(field));
|
||||
}
|
||||
argument => unimplemented!("expected field argument got {}", argument),
|
||||
}
|
||||
}
|
||||
Type::Boolean => {
|
||||
match self.enforce_expression(cs, function.get_name(), argument) {
|
||||
ResolvedValue::Boolean(bool) => {
|
||||
// Store argument as variable with {function_name}_{parameter name}
|
||||
let variable_name = new_scope_from_variable(
|
||||
function.get_name(),
|
||||
¶meter.variable,
|
||||
);
|
||||
self.store(variable_name, ResolvedValue::Boolean(bool));
|
||||
}
|
||||
argument => {
|
||||
unimplemented!("expected boolean argument got {}", argument)
|
||||
}
|
||||
}
|
||||
}
|
||||
ty => unimplemented!("parameter type {} not matched yet", ty),
|
||||
}
|
||||
});
|
||||
|
||||
// Evaluate function statements
|
||||
|
||||
let mut return_values = ResolvedValue::Return(vec![]);
|
||||
|
||||
function
|
||||
.statements
|
||||
.clone()
|
||||
.into_iter()
|
||||
.for_each(|statement| match statement {
|
||||
Statement::Definition(variable, expression) => {
|
||||
self.enforce_definition_statement(
|
||||
cs,
|
||||
function.get_name(),
|
||||
variable,
|
||||
expression,
|
||||
);
|
||||
}
|
||||
Statement::For(index, start, stop, statements) => {
|
||||
self.enforce_for_statement(
|
||||
cs,
|
||||
function.get_name(),
|
||||
index,
|
||||
start,
|
||||
stop,
|
||||
statements,
|
||||
);
|
||||
}
|
||||
Statement::Return(expressions) => {
|
||||
return_values =
|
||||
self.enforce_return_statement(cs, function.get_name(), expressions)
|
||||
}
|
||||
});
|
||||
|
||||
return_values
|
||||
}
|
||||
|
||||
fn enforce_import(&mut self, cs: &mut CS, import: Import) {
|
||||
// Resolve program file path
|
||||
let unparsed_file = fs::read_to_string(import.get_file()).expect("cannot read file");
|
||||
let mut file = ast::parse(&unparsed_file).expect("unsuccessful parse");
|
||||
|
||||
// generate ast from file
|
||||
let syntax_tree = ast::File::from_pest(&mut file).expect("infallible");
|
||||
|
||||
// generate aleo program from file
|
||||
let program = Program::from(syntax_tree);
|
||||
|
||||
// recursively evaluate program statements TODO: in file scope
|
||||
self.resolve_definitions(cs, program);
|
||||
|
||||
// store import under designated name
|
||||
// self.store(name, value)
|
||||
}
|
||||
|
||||
pub fn resolve_definitions(&mut self, cs: &mut CS, program: Program<F>) {
|
||||
program
|
||||
.imports
|
||||
.into_iter()
|
||||
.for_each(|import| self.enforce_import(cs, import));
|
||||
program
|
||||
.structs
|
||||
.into_iter()
|
||||
.for_each(|(variable, struct_def)| {
|
||||
self.store_variable(variable, ResolvedValue::StructDefinition(struct_def));
|
||||
});
|
||||
program
|
||||
.functions
|
||||
.into_iter()
|
||||
.for_each(|(function_name, function)| {
|
||||
self.store(function_name.0, ResolvedValue::Function(function));
|
||||
});
|
||||
}
|
||||
|
||||
pub fn generate_constraints(cs: &mut CS, program: Program<F>) {
|
||||
let mut resolved_program = ResolvedProgram::new();
|
||||
|
||||
resolved_program.resolve_definitions(cs, program);
|
||||
|
||||
let main = resolved_program
|
||||
.get(&"main".into())
|
||||
.expect("main function not defined");
|
||||
|
||||
let result = match main.clone() {
|
||||
ResolvedValue::Function(function) => {
|
||||
resolved_program.enforce_function(cs, function, vec![])
|
||||
}
|
||||
_ => unimplemented!("main must be a function"),
|
||||
};
|
||||
println!("\n {}", result);
|
||||
}
|
||||
}
|
200
src/aleo_program/constraints/expression.rs
Normal file
200
src/aleo_program/constraints/expression.rs
Normal file
@ -0,0 +1,200 @@
|
||||
use crate::aleo_program::constraints::{new_scope_from_variable, ResolvedProgram, ResolvedValue};
|
||||
use crate::aleo_program::{
|
||||
Expression, IntegerExpression, IntegerRangeOrExpression, StructMember, Variable,
|
||||
};
|
||||
|
||||
use snarkos_models::curves::{Field, PrimeField};
|
||||
use snarkos_models::gadgets::r1cs::ConstraintSystem;
|
||||
|
||||
impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ResolvedProgram<F, CS> {
|
||||
fn enforce_struct_expression(
|
||||
&mut self,
|
||||
cs: &mut CS,
|
||||
scope: String,
|
||||
variable: Variable<F>,
|
||||
members: Vec<StructMember<F>>,
|
||||
) -> ResolvedValue<F> {
|
||||
if let Some(resolved_value) = self.get_mut_variable(&variable) {
|
||||
match resolved_value {
|
||||
ResolvedValue::StructDefinition(struct_definition) => {
|
||||
struct_definition
|
||||
.fields
|
||||
.clone()
|
||||
.iter()
|
||||
.zip(members.clone().into_iter())
|
||||
.for_each(|(field, member)| {
|
||||
if field.variable != member.variable {
|
||||
unimplemented!("struct field variables do not match")
|
||||
}
|
||||
// Resolve and possibly enforce struct fields
|
||||
// do we need to store the results here?
|
||||
let _result =
|
||||
self.enforce_expression(cs, scope.clone(), member.expression);
|
||||
});
|
||||
|
||||
ResolvedValue::StructExpression(variable, members)
|
||||
}
|
||||
_ => unimplemented!("Inline struct type is not defined as a struct"),
|
||||
}
|
||||
} else {
|
||||
unimplemented!("Struct must be declared before it is used in an inline expression")
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn enforce_index(
|
||||
&mut self,
|
||||
cs: &mut CS,
|
||||
scope: String,
|
||||
index: IntegerExpression<F>,
|
||||
) -> usize {
|
||||
match self.enforce_integer_expression(cs, scope.clone(), index) {
|
||||
ResolvedValue::U32(number) => number.value.unwrap() as usize,
|
||||
value => unimplemented!("From index must resolve to a uint32, got {}", value),
|
||||
}
|
||||
}
|
||||
|
||||
fn enforce_array_access_expression(
|
||||
&mut self,
|
||||
cs: &mut CS,
|
||||
scope: String,
|
||||
array: Box<Expression<F>>,
|
||||
index: IntegerRangeOrExpression<F>,
|
||||
) -> ResolvedValue<F> {
|
||||
match self.enforce_expression(cs, scope.clone(), *array) {
|
||||
ResolvedValue::U32Array(field_array) => {
|
||||
match index {
|
||||
IntegerRangeOrExpression::Range(from, to) => {
|
||||
let from_resolved = match from {
|
||||
Some(from_index) => self.enforce_index(cs, scope.clone(), from_index),
|
||||
None => 0usize, // Array slice starts at index 0
|
||||
};
|
||||
let to_resolved = match to {
|
||||
Some(to_index) => self.enforce_index(cs, scope.clone(), to_index),
|
||||
None => field_array.len(), // Array slice ends at array length
|
||||
};
|
||||
ResolvedValue::U32Array(field_array[from_resolved..to_resolved].to_owned())
|
||||
}
|
||||
IntegerRangeOrExpression::Expression(index) => {
|
||||
let index_resolved = self.enforce_index(cs, scope.clone(), index);
|
||||
ResolvedValue::U32(field_array[index_resolved].to_owned())
|
||||
}
|
||||
}
|
||||
}
|
||||
ResolvedValue::BooleanArray(bool_array) => {
|
||||
match index {
|
||||
IntegerRangeOrExpression::Range(from, to) => {
|
||||
let from_resolved = match from {
|
||||
Some(from_index) => self.enforce_index(cs, scope.clone(), from_index),
|
||||
None => 0usize, // Array slice starts at index 0
|
||||
};
|
||||
let to_resolved = match to {
|
||||
Some(to_index) => self.enforce_index(cs, scope.clone(), to_index),
|
||||
None => bool_array.len(), // Array slice ends at array length
|
||||
};
|
||||
ResolvedValue::BooleanArray(
|
||||
bool_array[from_resolved..to_resolved].to_owned(),
|
||||
)
|
||||
}
|
||||
IntegerRangeOrExpression::Expression(index) => {
|
||||
let index_resolved = self.enforce_index(cs, scope.clone(), index);
|
||||
ResolvedValue::Boolean(bool_array[index_resolved].to_owned())
|
||||
}
|
||||
}
|
||||
}
|
||||
value => unimplemented!("Cannot access element of untyped array {}", value),
|
||||
}
|
||||
}
|
||||
|
||||
fn enforce_struct_access_expression(
|
||||
&mut self,
|
||||
cs: &mut CS,
|
||||
scope: String,
|
||||
struct_variable: Box<Expression<F>>,
|
||||
struct_member: Variable<F>,
|
||||
) -> ResolvedValue<F> {
|
||||
match self.enforce_expression(cs, scope.clone(), *struct_variable) {
|
||||
ResolvedValue::StructExpression(_name, members) => {
|
||||
let matched_member = members
|
||||
.into_iter()
|
||||
.find(|member| member.variable == struct_member);
|
||||
match matched_member {
|
||||
Some(member) => self.enforce_expression(cs, scope.clone(), member.expression),
|
||||
None => unimplemented!("Cannot access struct member {}", struct_member.name),
|
||||
}
|
||||
}
|
||||
value => unimplemented!("Cannot access element of untyped struct {}", value),
|
||||
}
|
||||
}
|
||||
|
||||
fn enforce_function_access_expression(
|
||||
&mut self,
|
||||
cs: &mut CS,
|
||||
scope: String,
|
||||
function: Box<Expression<F>>,
|
||||
arguments: Vec<Expression<F>>,
|
||||
) -> ResolvedValue<F> {
|
||||
match self.enforce_expression(cs, scope, *function) {
|
||||
ResolvedValue::Function(function) => self.enforce_function(cs, function, arguments),
|
||||
value => unimplemented!("Cannot call unknown function {}", value),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn enforce_expression(
|
||||
&mut self,
|
||||
cs: &mut CS,
|
||||
scope: String,
|
||||
expression: Expression<F>,
|
||||
) -> ResolvedValue<F> {
|
||||
match expression {
|
||||
Expression::Boolean(boolean_expression) => {
|
||||
self.enforce_boolean_expression(cs, scope, boolean_expression)
|
||||
}
|
||||
Expression::Integer(integer_expression) => {
|
||||
self.enforce_integer_expression(cs, scope, integer_expression)
|
||||
}
|
||||
Expression::FieldElement(field_expression) => {
|
||||
self.enforce_field_expression(cs, scope, field_expression)
|
||||
}
|
||||
Expression::Variable(unresolved_variable) => {
|
||||
let variable_name = new_scope_from_variable(scope, &unresolved_variable);
|
||||
|
||||
// Evaluate the variable name in the current function scope
|
||||
if self.contains_name(&variable_name) {
|
||||
// Reassigning variable to another variable
|
||||
self.get_mut(&variable_name).unwrap().clone()
|
||||
} else if self.contains_variable(&unresolved_variable) {
|
||||
// Check global scope (function and struct names)
|
||||
self.get_mut_variable(&unresolved_variable).unwrap().clone()
|
||||
} else {
|
||||
// The type of the unassigned variable depends on what is passed in
|
||||
if std::env::args()
|
||||
.nth(1)
|
||||
.expect("variable declaration not passed in")
|
||||
.parse::<bool>()
|
||||
.is_ok()
|
||||
{
|
||||
ResolvedValue::Boolean(self.bool_from_variable(
|
||||
cs,
|
||||
variable_name,
|
||||
unresolved_variable,
|
||||
))
|
||||
} else {
|
||||
self.integer_from_variable(cs, variable_name, unresolved_variable)
|
||||
}
|
||||
}
|
||||
}
|
||||
Expression::Struct(struct_name, members) => {
|
||||
self.enforce_struct_expression(cs, scope, struct_name, members)
|
||||
}
|
||||
Expression::ArrayAccess(array, index) => {
|
||||
self.enforce_array_access_expression(cs, scope, array, index)
|
||||
}
|
||||
Expression::StructMemberAccess(struct_variable, struct_member) => {
|
||||
self.enforce_struct_access_expression(cs, scope, struct_variable, struct_member)
|
||||
}
|
||||
Expression::FunctionCall(function, arguments) => {
|
||||
self.enforce_function_access_expression(cs, scope, function, arguments)
|
||||
} // expression => unimplemented!("expression not impl {}", expression),
|
||||
}
|
||||
}
|
||||
}
|
192
src/aleo_program/constraints/field_element.rs
Normal file
192
src/aleo_program/constraints/field_element.rs
Normal file
@ -0,0 +1,192 @@
|
||||
use crate::aleo_program::constraints::{new_scope_from_variable, ResolvedProgram, ResolvedValue};
|
||||
use crate::aleo_program::{FieldExpression, FieldSpreadOrExpression, Variable};
|
||||
|
||||
use snarkos_models::curves::{Field, PrimeField};
|
||||
use snarkos_models::gadgets::{r1cs::ConstraintSystem, utilities::boolean::Boolean};
|
||||
// use std::ops::{Add, Div, Mul, Neg, Sub};
|
||||
|
||||
impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ResolvedProgram<F, CS> {
|
||||
/// Constrain field elements
|
||||
|
||||
fn field_from_variable(&mut self, _cs: &mut CS, scope: String, variable: Variable<F>) -> F {
|
||||
// Evaluate variable name in current function scope
|
||||
let variable_name = new_scope_from_variable(scope, &variable);
|
||||
|
||||
if self.contains_name(&variable_name) {
|
||||
// TODO: return synthesis error: "assignment missing" here
|
||||
match self.get(&variable_name).unwrap() {
|
||||
ResolvedValue::FieldElement(field) => field.clone(),
|
||||
value => unimplemented!("expected field element, got {}", value),
|
||||
}
|
||||
} else {
|
||||
// TODO: remove this after resolving arguments
|
||||
let argument = std::env::args()
|
||||
.nth(1)
|
||||
.unwrap_or("1".into())
|
||||
.parse::<u32>()
|
||||
.unwrap();
|
||||
|
||||
println!(" argument passed to command line a = {:?}\n", argument);
|
||||
|
||||
// let a = 1;
|
||||
F::default()
|
||||
}
|
||||
}
|
||||
|
||||
fn get_field_value(&mut self, cs: &mut CS, scope: String, expression: FieldExpression<F>) -> F {
|
||||
match expression {
|
||||
FieldExpression::Variable(variable) => self.field_from_variable(cs, scope, variable),
|
||||
FieldExpression::Number(element) => element,
|
||||
expression => match self.enforce_field_expression(cs, scope, expression) {
|
||||
ResolvedValue::FieldElement(element) => element,
|
||||
value => unimplemented!("expected field element, got {}", value),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn enforce_field_equality(
|
||||
&mut self,
|
||||
cs: &mut CS,
|
||||
scope: String,
|
||||
left: FieldExpression<F>,
|
||||
right: FieldExpression<F>,
|
||||
) -> Boolean {
|
||||
let left = self.get_field_value(cs, scope.clone(), left);
|
||||
let right = self.get_field_value(cs, scope.clone(), right);
|
||||
|
||||
Boolean::Constant(left.eq(&right))
|
||||
}
|
||||
|
||||
fn enforce_field_add(
|
||||
&mut self,
|
||||
cs: &mut CS,
|
||||
scope: String,
|
||||
left: FieldExpression<F>,
|
||||
right: FieldExpression<F>,
|
||||
) -> ResolvedValue<F> {
|
||||
let left = self.get_field_value(cs, scope.clone(), left);
|
||||
let right = self.get_field_value(cs, scope.clone(), right);
|
||||
|
||||
ResolvedValue::FieldElement(left.add(&right))
|
||||
}
|
||||
|
||||
fn enforce_field_sub(
|
||||
&mut self,
|
||||
cs: &mut CS,
|
||||
scope: String,
|
||||
left: FieldExpression<F>,
|
||||
right: FieldExpression<F>,
|
||||
) -> ResolvedValue<F> {
|
||||
let left = self.get_field_value(cs, scope.clone(), left);
|
||||
let right = self.get_field_value(cs, scope.clone(), right);
|
||||
|
||||
ResolvedValue::FieldElement(left.sub(&right))
|
||||
}
|
||||
|
||||
fn enforce_field_mul(
|
||||
&mut self,
|
||||
cs: &mut CS,
|
||||
scope: String,
|
||||
left: FieldExpression<F>,
|
||||
right: FieldExpression<F>,
|
||||
) -> ResolvedValue<F> {
|
||||
let left = self.get_field_value(cs, scope.clone(), left);
|
||||
let right = self.get_field_value(cs, scope.clone(), right);
|
||||
|
||||
ResolvedValue::FieldElement(left.mul(&right))
|
||||
}
|
||||
|
||||
fn enforce_field_div(
|
||||
&mut self,
|
||||
cs: &mut CS,
|
||||
scope: String,
|
||||
left: FieldExpression<F>,
|
||||
right: FieldExpression<F>,
|
||||
) -> ResolvedValue<F> {
|
||||
let left = self.get_field_value(cs, scope.clone(), left);
|
||||
let right = self.get_field_value(cs, scope.clone(), right);
|
||||
|
||||
ResolvedValue::FieldElement(left.div(&right))
|
||||
}
|
||||
|
||||
fn enforce_field_pow(
|
||||
&mut self,
|
||||
_cs: &mut CS,
|
||||
_scope: String,
|
||||
_left: FieldExpression<F>,
|
||||
_right: FieldExpression<F>,
|
||||
) -> ResolvedValue<F> {
|
||||
unimplemented!("field element exponentiation not supported")
|
||||
// let left = self.get_field_value(cs, scope.clone(), left);
|
||||
// let right = self.get_field_value(cs, scope.clone(), right);
|
||||
//
|
||||
// ResolvedValue::FieldElement(left.pow(&right))
|
||||
}
|
||||
|
||||
pub(crate) fn enforce_field_expression(
|
||||
&mut self,
|
||||
cs: &mut CS,
|
||||
scope: String,
|
||||
expression: FieldExpression<F>,
|
||||
) -> ResolvedValue<F> {
|
||||
match expression {
|
||||
FieldExpression::Variable(variable) => {
|
||||
ResolvedValue::FieldElement(self.field_from_variable(cs, scope, variable))
|
||||
}
|
||||
FieldExpression::Number(field) => ResolvedValue::FieldElement(field),
|
||||
FieldExpression::Add(left, right) => self.enforce_field_add(cs, scope, *left, *right),
|
||||
FieldExpression::Sub(left, right) => self.enforce_field_sub(cs, scope, *left, *right),
|
||||
FieldExpression::Mul(left, right) => self.enforce_field_mul(cs, scope, *left, *right),
|
||||
FieldExpression::Div(left, right) => self.enforce_field_div(cs, scope, *left, *right),
|
||||
FieldExpression::Pow(left, right) => self.enforce_field_pow(cs, scope, *left, *right),
|
||||
FieldExpression::IfElse(first, second, third) => {
|
||||
let resolved_first =
|
||||
match self.enforce_boolean_expression(cs, scope.clone(), *first) {
|
||||
ResolvedValue::Boolean(resolved) => resolved,
|
||||
_ => unimplemented!("if else conditional must resolve to boolean"),
|
||||
};
|
||||
|
||||
if resolved_first.eq(&Boolean::Constant(true)) {
|
||||
self.enforce_field_expression(cs, scope, *second)
|
||||
} else {
|
||||
self.enforce_field_expression(cs, scope, *third)
|
||||
}
|
||||
}
|
||||
FieldExpression::Array(array) => {
|
||||
let mut result = vec![];
|
||||
array.into_iter().for_each(|element| match *element {
|
||||
FieldSpreadOrExpression::Spread(spread) => match spread {
|
||||
FieldExpression::Variable(variable) => {
|
||||
let array_name = new_scope_from_variable(scope.clone(), &variable);
|
||||
match self.get(&array_name) {
|
||||
Some(value) => match value {
|
||||
ResolvedValue::FieldElementArray(array) => {
|
||||
result.extend(array.clone())
|
||||
}
|
||||
value => unimplemented!(
|
||||
"spreads only implemented for arrays, got {}",
|
||||
value
|
||||
),
|
||||
},
|
||||
None => unimplemented!(
|
||||
"cannot copy elements from array that does not exist {}",
|
||||
variable.name
|
||||
),
|
||||
}
|
||||
}
|
||||
value => {
|
||||
unimplemented!("spreads only implemented for arrays, got {}", value)
|
||||
}
|
||||
},
|
||||
FieldSpreadOrExpression::Expression(expression) => {
|
||||
match self.enforce_field_expression(cs, scope.clone(), expression) {
|
||||
ResolvedValue::FieldElement(value) => result.push(value),
|
||||
_ => unimplemented!("cannot resolve field"),
|
||||
}
|
||||
}
|
||||
});
|
||||
ResolvedValue::FieldElementArray(result)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
320
src/aleo_program/constraints/integer.rs
Normal file
320
src/aleo_program/constraints/integer.rs
Normal file
@ -0,0 +1,320 @@
|
||||
use crate::aleo_program::constraints::{new_scope_from_variable, ResolvedProgram, ResolvedValue};
|
||||
use crate::aleo_program::{Integer, IntegerExpression, IntegerSpreadOrExpression, Variable};
|
||||
|
||||
use snarkos_models::curves::{Field, PrimeField};
|
||||
use snarkos_models::gadgets::{
|
||||
r1cs::ConstraintSystem,
|
||||
utilities::{alloc::AllocGadget, boolean::Boolean, eq::ConditionalEqGadget, uint32::UInt32},
|
||||
};
|
||||
|
||||
impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ResolvedProgram<F, CS> {
|
||||
pub(crate) fn integer_from_variable(
|
||||
&mut self,
|
||||
cs: &mut CS,
|
||||
scope: String,
|
||||
variable: Variable<F>,
|
||||
) -> ResolvedValue<F> {
|
||||
// Evaluate variable name in current function scope
|
||||
let variable_name = new_scope_from_variable(scope, &variable);
|
||||
|
||||
if self.contains_name(&variable_name) {
|
||||
// TODO: return synthesis error: "assignment missing" here
|
||||
self.get(&variable_name).unwrap().clone()
|
||||
} else {
|
||||
// TODO: remove this after resolving arguments
|
||||
let argument = std::env::args()
|
||||
.nth(1)
|
||||
.unwrap_or("1".into())
|
||||
.parse::<u32>()
|
||||
.unwrap();
|
||||
|
||||
println!(" argument passed to command line a = {:?}\n", argument);
|
||||
|
||||
// let a = 1;
|
||||
ResolvedValue::U32(UInt32::alloc(cs.ns(|| variable.name), Some(argument)).unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
fn get_integer_constant(integer: Integer) -> ResolvedValue<F> {
|
||||
match integer {
|
||||
Integer::U32(u32_value) => ResolvedValue::U32(UInt32::constant(u32_value)),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_integer_value(
|
||||
&mut self,
|
||||
cs: &mut CS,
|
||||
scope: String,
|
||||
expression: IntegerExpression<F>,
|
||||
) -> ResolvedValue<F> {
|
||||
match expression {
|
||||
IntegerExpression::Variable(variable) => {
|
||||
self.integer_from_variable(cs, scope, variable)
|
||||
}
|
||||
IntegerExpression::Number(number) => Self::get_integer_constant(number),
|
||||
expression => self.enforce_integer_expression(cs, scope, expression),
|
||||
}
|
||||
}
|
||||
|
||||
fn enforce_u32_equality(cs: &mut CS, left: UInt32, right: UInt32) -> Boolean {
|
||||
left.conditional_enforce_equal(
|
||||
cs.ns(|| format!("enforce field equal")),
|
||||
&right,
|
||||
&Boolean::Constant(true),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
Boolean::Constant(true)
|
||||
}
|
||||
|
||||
pub(crate) fn enforce_integer_equality(
|
||||
&mut self,
|
||||
cs: &mut CS,
|
||||
scope: String,
|
||||
left: IntegerExpression<F>,
|
||||
right: IntegerExpression<F>,
|
||||
) -> Boolean {
|
||||
let left = self.get_integer_value(cs, scope.clone(), left);
|
||||
let right = self.get_integer_value(cs, scope.clone(), right);
|
||||
|
||||
match (left, right) {
|
||||
(ResolvedValue::U32(left_u32), ResolvedValue::U32(right_u32)) => {
|
||||
Self::enforce_u32_equality(cs, left_u32, right_u32)
|
||||
}
|
||||
(left_int, right_int) => {
|
||||
unimplemented!("equality not impl between {} == {}", left_int, right_int)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn enforce_u32_add(cs: &mut CS, left: UInt32, right: UInt32) -> ResolvedValue<F> {
|
||||
ResolvedValue::U32(
|
||||
UInt32::addmany(
|
||||
cs.ns(|| format!("enforce {} + {}", left.value.unwrap(), right.value.unwrap())),
|
||||
&[left, right],
|
||||
)
|
||||
.unwrap(),
|
||||
)
|
||||
}
|
||||
|
||||
fn enforce_integer_add(
|
||||
&mut self,
|
||||
cs: &mut CS,
|
||||
scope: String,
|
||||
left: IntegerExpression<F>,
|
||||
right: IntegerExpression<F>,
|
||||
) -> ResolvedValue<F> {
|
||||
let left = self.get_integer_value(cs, scope.clone(), left);
|
||||
let right = self.get_integer_value(cs, scope.clone(), right);
|
||||
|
||||
match (left, right) {
|
||||
(ResolvedValue::U32(left_u32), ResolvedValue::U32(right_u32)) => {
|
||||
Self::enforce_u32_add(cs, left_u32, right_u32)
|
||||
}
|
||||
(left_int, right_int) => {
|
||||
unimplemented!("add not impl between {} + {}", left_int, right_int)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn enforce_u32_sub(cs: &mut CS, left: UInt32, right: UInt32) -> ResolvedValue<F> {
|
||||
ResolvedValue::U32(
|
||||
left.sub(
|
||||
cs.ns(|| format!("enforce {} - {}", left.value.unwrap(), right.value.unwrap())),
|
||||
&right,
|
||||
)
|
||||
.unwrap(),
|
||||
)
|
||||
}
|
||||
|
||||
fn enforce_integer_sub(
|
||||
&mut self,
|
||||
cs: &mut CS,
|
||||
scope: String,
|
||||
left: IntegerExpression<F>,
|
||||
right: IntegerExpression<F>,
|
||||
) -> ResolvedValue<F> {
|
||||
let left = self.get_integer_value(cs, scope.clone(), left);
|
||||
let right = self.get_integer_value(cs, scope.clone(), right);
|
||||
|
||||
match (left, right) {
|
||||
(ResolvedValue::U32(left_u32), ResolvedValue::U32(right_u32)) => {
|
||||
Self::enforce_u32_sub(cs, left_u32, right_u32)
|
||||
}
|
||||
(left_int, right_int) => {
|
||||
unimplemented!("add not impl between {} + {}", left_int, right_int)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn enforce_u32_mul(cs: &mut CS, left: UInt32, right: UInt32) -> ResolvedValue<F> {
|
||||
ResolvedValue::U32(
|
||||
left.mul(
|
||||
cs.ns(|| format!("enforce {} * {}", left.value.unwrap(), right.value.unwrap())),
|
||||
&right,
|
||||
)
|
||||
.unwrap(),
|
||||
)
|
||||
}
|
||||
|
||||
fn enforce_integer_mul(
|
||||
&mut self,
|
||||
cs: &mut CS,
|
||||
scope: String,
|
||||
left: IntegerExpression<F>,
|
||||
right: IntegerExpression<F>,
|
||||
) -> ResolvedValue<F> {
|
||||
let left = self.get_integer_value(cs, scope.clone(), left);
|
||||
let right = self.get_integer_value(cs, scope.clone(), right);
|
||||
|
||||
match (left, right) {
|
||||
(ResolvedValue::U32(left_u32), ResolvedValue::U32(right_u32)) => {
|
||||
Self::enforce_u32_mul(cs, left_u32, right_u32)
|
||||
}
|
||||
(left_int, right_int) => {
|
||||
unimplemented!("add not impl between {} + {}", left_int, right_int)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn enforce_u32_div(cs: &mut CS, left: UInt32, right: UInt32) -> ResolvedValue<F> {
|
||||
ResolvedValue::U32(
|
||||
left.div(
|
||||
cs.ns(|| format!("enforce {} / {}", left.value.unwrap(), right.value.unwrap())),
|
||||
&right,
|
||||
)
|
||||
.unwrap(),
|
||||
)
|
||||
}
|
||||
|
||||
fn enforce_integer_div(
|
||||
&mut self,
|
||||
cs: &mut CS,
|
||||
scope: String,
|
||||
left: IntegerExpression<F>,
|
||||
right: IntegerExpression<F>,
|
||||
) -> ResolvedValue<F> {
|
||||
let left = self.get_integer_value(cs, scope.clone(), left);
|
||||
let right = self.get_integer_value(cs, scope.clone(), right);
|
||||
|
||||
match (left, right) {
|
||||
(ResolvedValue::U32(left_u32), ResolvedValue::U32(right_u32)) => {
|
||||
Self::enforce_u32_div(cs, left_u32, right_u32)
|
||||
}
|
||||
(left_int, right_int) => {
|
||||
unimplemented!("add not impl between {} + {}", left_int, right_int)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn enforce_u32_pow(cs: &mut CS, left: UInt32, right: UInt32) -> ResolvedValue<F> {
|
||||
ResolvedValue::U32(
|
||||
left.pow(
|
||||
cs.ns(|| {
|
||||
format!(
|
||||
"enforce {} ** {}",
|
||||
left.value.unwrap(),
|
||||
right.value.unwrap()
|
||||
)
|
||||
}),
|
||||
&right,
|
||||
)
|
||||
.unwrap(),
|
||||
)
|
||||
}
|
||||
|
||||
fn enforce_integer_pow(
|
||||
&mut self,
|
||||
cs: &mut CS,
|
||||
scope: String,
|
||||
left: IntegerExpression<F>,
|
||||
right: IntegerExpression<F>,
|
||||
) -> ResolvedValue<F> {
|
||||
let left = self.get_integer_value(cs, scope.clone(), left);
|
||||
let right = self.get_integer_value(cs, scope.clone(), right);
|
||||
|
||||
match (left, right) {
|
||||
(ResolvedValue::U32(left_u32), ResolvedValue::U32(right_u32)) => {
|
||||
Self::enforce_u32_pow(cs, left_u32, right_u32)
|
||||
}
|
||||
(left_int, right_int) => {
|
||||
unimplemented!("add not impl between {} + {}", left_int, right_int)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn enforce_integer_expression(
|
||||
&mut self,
|
||||
cs: &mut CS,
|
||||
scope: String,
|
||||
expression: IntegerExpression<F>,
|
||||
) -> ResolvedValue<F> {
|
||||
match expression {
|
||||
IntegerExpression::Variable(variable) => {
|
||||
self.integer_from_variable(cs, scope, variable)
|
||||
}
|
||||
IntegerExpression::Number(number) => Self::get_integer_constant(number),
|
||||
IntegerExpression::Add(left, right) => {
|
||||
self.enforce_integer_add(cs, scope, *left, *right)
|
||||
}
|
||||
IntegerExpression::Sub(left, right) => {
|
||||
self.enforce_integer_sub(cs, scope, *left, *right)
|
||||
}
|
||||
IntegerExpression::Mul(left, right) => {
|
||||
self.enforce_integer_mul(cs, scope, *left, *right)
|
||||
}
|
||||
IntegerExpression::Div(left, right) => {
|
||||
self.enforce_integer_div(cs, scope, *left, *right)
|
||||
}
|
||||
IntegerExpression::Pow(left, right) => {
|
||||
self.enforce_integer_pow(cs, scope, *left, *right)
|
||||
}
|
||||
IntegerExpression::IfElse(first, second, third) => {
|
||||
let resolved_first =
|
||||
match self.enforce_boolean_expression(cs, scope.clone(), *first) {
|
||||
ResolvedValue::Boolean(resolved) => resolved,
|
||||
_ => unimplemented!("if else conditional must resolve to boolean"),
|
||||
};
|
||||
|
||||
if resolved_first.eq(&Boolean::Constant(true)) {
|
||||
self.enforce_integer_expression(cs, scope, *second)
|
||||
} else {
|
||||
self.enforce_integer_expression(cs, scope, *third)
|
||||
}
|
||||
}
|
||||
IntegerExpression::Array(array) => {
|
||||
let mut result = vec![];
|
||||
array.into_iter().for_each(|element| match *element {
|
||||
IntegerSpreadOrExpression::Spread(spread) => match spread {
|
||||
IntegerExpression::Variable(variable) => {
|
||||
let array_name = new_scope_from_variable(scope.clone(), &variable);
|
||||
match self.get(&array_name) {
|
||||
Some(value) => match value {
|
||||
ResolvedValue::U32Array(array) => result.extend(array.clone()),
|
||||
value => unimplemented!(
|
||||
"spreads only implemented for arrays, got {}",
|
||||
value
|
||||
),
|
||||
},
|
||||
None => unimplemented!(
|
||||
"cannot copy elements from array that does not exist {}",
|
||||
variable.name
|
||||
),
|
||||
}
|
||||
}
|
||||
value => {
|
||||
unimplemented!("spreads only implemented for arrays, got {}", value)
|
||||
}
|
||||
},
|
||||
IntegerSpreadOrExpression::Expression(expression) => {
|
||||
match self.enforce_integer_expression(cs, scope.clone(), expression) {
|
||||
ResolvedValue::U32(value) => result.push(value),
|
||||
_ => unimplemented!("cannot resolve field"),
|
||||
}
|
||||
}
|
||||
});
|
||||
ResolvedValue::U32Array(result)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
29
src/aleo_program/constraints/mod.rs
Normal file
29
src/aleo_program/constraints/mod.rs
Normal file
@ -0,0 +1,29 @@
|
||||
//! Module containing methods to enforce constraints in an aleo program
|
||||
//!
|
||||
//! @file constraints/mod.rs
|
||||
//! @author Collin Chin <collin@aleo.org>
|
||||
//! @date 2020
|
||||
|
||||
pub mod boolean;
|
||||
pub use boolean::*;
|
||||
|
||||
pub mod constraints;
|
||||
pub use constraints::*;
|
||||
|
||||
pub mod expression;
|
||||
pub use expression::*;
|
||||
|
||||
pub mod integer;
|
||||
pub use integer::*;
|
||||
|
||||
pub mod field_element;
|
||||
pub use field_element::*;
|
||||
|
||||
pub mod resolved_program;
|
||||
pub use resolved_program::*;
|
||||
|
||||
pub mod resolved_value;
|
||||
pub use resolved_value::*;
|
||||
|
||||
pub mod statement;
|
||||
pub use statement::*;
|
63
src/aleo_program/constraints/resolved_program.rs
Normal file
63
src/aleo_program/constraints/resolved_program.rs
Normal file
@ -0,0 +1,63 @@
|
||||
use crate::aleo_program::constraints::ResolvedValue;
|
||||
use crate::aleo_program::types::Variable;
|
||||
|
||||
use snarkos_models::curves::{Field, PrimeField};
|
||||
use snarkos_models::gadgets::r1cs::ConstraintSystem;
|
||||
use std::collections::HashMap;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
pub struct ResolvedProgram<F: Field + PrimeField, CS: ConstraintSystem<F>> {
|
||||
pub resolved_names: HashMap<String, ResolvedValue<F>>,
|
||||
pub _cs: PhantomData<CS>,
|
||||
}
|
||||
|
||||
pub fn new_scope(outer: String, inner: String) -> String {
|
||||
format!("{}_{}", outer, inner)
|
||||
}
|
||||
|
||||
pub fn new_scope_from_variable<F: Field + PrimeField>(
|
||||
outer: String,
|
||||
inner: &Variable<F>,
|
||||
) -> String {
|
||||
new_scope(outer, inner.name.clone())
|
||||
}
|
||||
|
||||
impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ResolvedProgram<F, CS> {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
resolved_names: HashMap::new(),
|
||||
_cs: PhantomData::<CS>,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn store(&mut self, name: String, value: ResolvedValue<F>) {
|
||||
self.resolved_names.insert(name, value);
|
||||
}
|
||||
|
||||
pub(crate) fn store_variable(&mut self, variable: Variable<F>, value: ResolvedValue<F>) {
|
||||
self.store(variable.name, value);
|
||||
}
|
||||
|
||||
pub(crate) fn contains_name(&self, name: &String) -> bool {
|
||||
self.resolved_names.contains_key(name)
|
||||
}
|
||||
|
||||
pub(crate) fn contains_variable(&self, variable: &Variable<F>) -> bool {
|
||||
self.contains_name(&variable.name)
|
||||
}
|
||||
|
||||
pub(crate) fn get(&self, name: &String) -> Option<&ResolvedValue<F>> {
|
||||
self.resolved_names.get(name)
|
||||
}
|
||||
|
||||
pub(crate) fn get_mut(&mut self, name: &String) -> Option<&mut ResolvedValue<F>> {
|
||||
self.resolved_names.get_mut(name)
|
||||
}
|
||||
|
||||
pub(crate) fn get_mut_variable(
|
||||
&mut self,
|
||||
variable: &Variable<F>,
|
||||
) -> Option<&mut ResolvedValue<F>> {
|
||||
self.get_mut(&variable.name)
|
||||
}
|
||||
}
|
85
src/aleo_program/constraints/resolved_value.rs
Normal file
85
src/aleo_program/constraints/resolved_value.rs
Normal file
@ -0,0 +1,85 @@
|
||||
use crate::aleo_program::types::{Function, Struct, StructMember, Variable};
|
||||
|
||||
use snarkos_models::curves::{Field, PrimeField};
|
||||
use snarkos_models::gadgets::{utilities::boolean::Boolean, utilities::uint32::UInt32};
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum ResolvedValue<F: Field + PrimeField> {
|
||||
U32(UInt32),
|
||||
U32Array(Vec<UInt32>),
|
||||
FieldElement(F),
|
||||
FieldElementArray(Vec<F>),
|
||||
Boolean(Boolean),
|
||||
BooleanArray(Vec<Boolean>),
|
||||
StructDefinition(Struct<F>),
|
||||
StructExpression(Variable<F>, Vec<StructMember<F>>),
|
||||
Function(Function<F>),
|
||||
Return(Vec<ResolvedValue<F>>), // add Null for function returns
|
||||
}
|
||||
|
||||
impl<F: Field + PrimeField> fmt::Display for ResolvedValue<F> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self {
|
||||
ResolvedValue::U32(ref value) => write!(f, "{}", value.value.unwrap()),
|
||||
ResolvedValue::U32Array(ref array) => {
|
||||
write!(f, "[")?;
|
||||
for (i, e) in array.iter().enumerate() {
|
||||
write!(f, "{}", e.value.unwrap())?;
|
||||
if i < array.len() - 1 {
|
||||
write!(f, ", ")?;
|
||||
}
|
||||
}
|
||||
write!(f, "]")
|
||||
}
|
||||
ResolvedValue::FieldElement(ref value) => write!(f, "{}", value),
|
||||
ResolvedValue::FieldElementArray(ref array) => {
|
||||
write!(f, "[")?;
|
||||
for (i, e) in array.iter().enumerate() {
|
||||
write!(f, "{}", e)?;
|
||||
if i < array.len() - 1 {
|
||||
write!(f, ", ")?;
|
||||
}
|
||||
}
|
||||
write!(f, "]")
|
||||
}
|
||||
ResolvedValue::Boolean(ref value) => write!(f, "{}", value.get_value().unwrap()),
|
||||
ResolvedValue::BooleanArray(ref array) => {
|
||||
write!(f, "[")?;
|
||||
for (i, e) in array.iter().enumerate() {
|
||||
write!(f, "{}", e.get_value().unwrap())?;
|
||||
if i < array.len() - 1 {
|
||||
write!(f, ", ")?;
|
||||
}
|
||||
}
|
||||
write!(f, "]")
|
||||
}
|
||||
ResolvedValue::StructExpression(ref variable, ref members) => {
|
||||
write!(f, "{} {{", variable)?;
|
||||
for (i, member) in members.iter().enumerate() {
|
||||
write!(f, "{}: {}", member.variable, member.expression)?;
|
||||
if i < members.len() - 1 {
|
||||
write!(f, ", ")?;
|
||||
}
|
||||
}
|
||||
write!(f, "}}")
|
||||
}
|
||||
ResolvedValue::Return(ref values) => {
|
||||
write!(f, "Return values : [")?;
|
||||
for (i, value) in values.iter().enumerate() {
|
||||
write!(f, "{}", value)?;
|
||||
if i < values.len() - 1 {
|
||||
write!(f, ", ")?;
|
||||
}
|
||||
}
|
||||
write!(f, "]")
|
||||
}
|
||||
ResolvedValue::StructDefinition(ref _definition) => {
|
||||
unimplemented!("cannot return struct definition in program")
|
||||
}
|
||||
ResolvedValue::Function(ref _function) => {
|
||||
unimplemented!("cannot return function definition in program")
|
||||
} // _ => unimplemented!("display not impl for value"),
|
||||
}
|
||||
}
|
||||
}
|
194
src/aleo_program/constraints/statement.rs
Normal file
194
src/aleo_program/constraints/statement.rs
Normal file
@ -0,0 +1,194 @@
|
||||
use crate::aleo_program::constraints::{new_scope_from_variable, ResolvedProgram, ResolvedValue};
|
||||
use crate::aleo_program::{
|
||||
Assignee, Expression, IntegerExpression, IntegerRangeOrExpression, Statement, Variable,
|
||||
};
|
||||
|
||||
use snarkos_models::curves::{Field, PrimeField};
|
||||
use snarkos_models::gadgets::{r1cs::ConstraintSystem, utilities::uint32::UInt32};
|
||||
|
||||
impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ResolvedProgram<F, CS> {
|
||||
fn resolve_assignee(&mut self, scope: String, assignee: Assignee<F>) -> String {
|
||||
match assignee {
|
||||
Assignee::Variable(name) => new_scope_from_variable(scope, &name),
|
||||
Assignee::Array(array, _index) => self.resolve_assignee(scope, *array),
|
||||
Assignee::StructMember(struct_variable, _member) => {
|
||||
self.resolve_assignee(scope, *struct_variable)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn enforce_definition_statement(
|
||||
&mut self,
|
||||
cs: &mut CS,
|
||||
scope: String,
|
||||
assignee: Assignee<F>,
|
||||
expression: Expression<F>,
|
||||
) {
|
||||
// Create or modify the lhs variable in the current function scope
|
||||
match assignee {
|
||||
Assignee::Variable(name) => {
|
||||
// Store the variable in the current scope
|
||||
let definition_name = new_scope_from_variable(scope.clone(), &name);
|
||||
|
||||
// Evaluate the rhs expression in the current function scope
|
||||
let result = self.enforce_expression(cs, scope, expression);
|
||||
|
||||
self.store(definition_name, result);
|
||||
}
|
||||
Assignee::Array(array, index_expression) => {
|
||||
// Evaluate the rhs expression in the current function scope
|
||||
let result = &mut self.enforce_expression(cs, scope.clone(), expression);
|
||||
|
||||
// Check that array exists
|
||||
let expected_array_name = self.resolve_assignee(scope.clone(), *array);
|
||||
|
||||
// Resolve index so we know if we are assigning to a single value or a range of values
|
||||
match index_expression {
|
||||
IntegerRangeOrExpression::Expression(index) => {
|
||||
let index = self.enforce_index(cs, scope.clone(), index);
|
||||
|
||||
// Modify the single value of the array in place
|
||||
match self.get_mut(&expected_array_name) {
|
||||
Some(value) => match (value, result) {
|
||||
(ResolvedValue::U32Array(old), ResolvedValue::U32(new)) => {
|
||||
old[index] = new.to_owned();
|
||||
}
|
||||
(ResolvedValue::BooleanArray(old), ResolvedValue::Boolean(new)) => {
|
||||
old[index] = new.to_owned();
|
||||
}
|
||||
_ => {
|
||||
unimplemented!("Cannot assign single index to array of values ")
|
||||
}
|
||||
},
|
||||
None => unimplemented!(
|
||||
"tried to assign to unknown array {}",
|
||||
expected_array_name
|
||||
),
|
||||
}
|
||||
}
|
||||
IntegerRangeOrExpression::Range(from, to) => {
|
||||
let from_index = match from {
|
||||
Some(expression) => self.enforce_index(cs, scope.clone(), expression),
|
||||
None => 0usize,
|
||||
};
|
||||
let to_index_option = match to {
|
||||
Some(expression) => {
|
||||
Some(self.enforce_index(cs, scope.clone(), expression))
|
||||
}
|
||||
None => None,
|
||||
};
|
||||
|
||||
// Modify the range of values of the array in place
|
||||
match self.get_mut(&expected_array_name) {
|
||||
Some(value) => match (value, result) {
|
||||
(ResolvedValue::U32Array(old), ResolvedValue::U32Array(new)) => {
|
||||
let to_index = to_index_option.unwrap_or(old.len());
|
||||
old.splice(from_index..to_index, new.iter().cloned());
|
||||
}
|
||||
(
|
||||
ResolvedValue::BooleanArray(old),
|
||||
ResolvedValue::BooleanArray(new),
|
||||
) => {
|
||||
let to_index = to_index_option.unwrap_or(old.len());
|
||||
old.splice(from_index..to_index, new.iter().cloned());
|
||||
}
|
||||
_ => unimplemented!(
|
||||
"Cannot assign a range of array values to single value"
|
||||
),
|
||||
},
|
||||
None => unimplemented!(
|
||||
"tried to assign to unknown array {}",
|
||||
expected_array_name
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Assignee::StructMember(struct_variable, struct_member) => {
|
||||
// Check that struct exists
|
||||
let expected_struct_name = self.resolve_assignee(scope.clone(), *struct_variable);
|
||||
|
||||
match self.get_mut(&expected_struct_name) {
|
||||
Some(value) => match value {
|
||||
ResolvedValue::StructExpression(_variable, members) => {
|
||||
// Modify the struct member in place
|
||||
let matched_member = members
|
||||
.into_iter()
|
||||
.find(|member| member.variable == struct_member);
|
||||
match matched_member {
|
||||
Some(mut member) => member.expression = expression,
|
||||
None => unimplemented!(
|
||||
"struct member {} does not exist in {}",
|
||||
struct_member,
|
||||
expected_struct_name
|
||||
),
|
||||
}
|
||||
}
|
||||
_ => unimplemented!(
|
||||
"tried to assign to unknown struct {}",
|
||||
expected_struct_name
|
||||
),
|
||||
},
|
||||
None => {
|
||||
unimplemented!("tried to assign to unknown struct {}", expected_struct_name)
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub(crate) fn enforce_return_statement(
|
||||
&mut self,
|
||||
cs: &mut CS,
|
||||
scope: String,
|
||||
statements: Vec<Expression<F>>,
|
||||
) -> ResolvedValue<F> {
|
||||
ResolvedValue::Return(
|
||||
statements
|
||||
.into_iter()
|
||||
.map(|expression| self.enforce_expression(cs, scope.clone(), expression))
|
||||
.collect::<Vec<ResolvedValue<F>>>(),
|
||||
)
|
||||
}
|
||||
|
||||
fn enforce_statement(&mut self, cs: &mut CS, scope: String, statement: Statement<F>) {
|
||||
match statement {
|
||||
Statement::Definition(variable, expression) => {
|
||||
self.enforce_definition_statement(cs, scope, variable, expression);
|
||||
}
|
||||
Statement::For(index, start, stop, statements) => {
|
||||
self.enforce_for_statement(cs, scope, index, start, stop, statements);
|
||||
}
|
||||
Statement::Return(statements) => {
|
||||
// TODO: add support for early termination
|
||||
let _res = self.enforce_return_statement(cs, scope, statements);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub(crate) fn enforce_for_statement(
|
||||
&mut self,
|
||||
cs: &mut CS,
|
||||
scope: String,
|
||||
index: Variable<F>,
|
||||
start: IntegerExpression<F>,
|
||||
stop: IntegerExpression<F>,
|
||||
statements: Vec<Statement<F>>,
|
||||
) {
|
||||
let start_index = self.enforce_index(cs, scope.clone(), start);
|
||||
let stop_index = self.enforce_index(cs, scope.clone(), stop);
|
||||
|
||||
for i in start_index..stop_index {
|
||||
// Store index in current function scope.
|
||||
// For loop scope is not implemented.
|
||||
let index_name = new_scope_from_variable(scope.clone(), &index);
|
||||
self.store(index_name, ResolvedValue::U32(UInt32::constant(i as u32)));
|
||||
|
||||
// Evaluate statements
|
||||
statements
|
||||
.clone()
|
||||
.into_iter()
|
||||
.for_each(|statement| self.enforce_statement(cs, scope.clone(), statement));
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
//! Module containing structs and types that make up a zokrates_program.
|
||||
//! Module containing structs and types that make up an aleo program.
|
||||
//!
|
||||
//! @file zokrates_program.rs
|
||||
//! @file aleo_program/mod.rs
|
||||
//! @author Collin Chin <collin@aleo.org>
|
||||
//! @date 2020
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user