mirror of
https://github.com/ProvableHQ/leo.git
synced 2024-11-30 23:33:27 +03:00
add function modules
This commit is contained in:
parent
24df97b9a3
commit
cce056be5d
@ -1,103 +1,29 @@
|
|||||||
//! Methods to enforce functions with arguments in a compiled Leo program.
|
//! Enforces constraints on a function in a compiled Leo program.
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
address::Address,
|
errors::FunctionError,
|
||||||
errors::{FunctionError, StatementError},
|
|
||||||
program::{new_scope, ConstrainedProgram},
|
program::{new_scope, ConstrainedProgram},
|
||||||
value::{
|
value::ConstrainedValue,
|
||||||
boolean::input::bool_from_input,
|
|
||||||
field::input::field_from_input,
|
|
||||||
group::input::group_from_input,
|
|
||||||
ConstrainedValue,
|
|
||||||
},
|
|
||||||
GroupType,
|
GroupType,
|
||||||
Integer,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use leo_types::{Expression, Function, InputValue, Span, Type};
|
use leo_types::{Expression, Function, Span};
|
||||||
|
|
||||||
use snarkos_models::{
|
use snarkos_models::{
|
||||||
curves::{Field, PrimeField},
|
curves::{Field, PrimeField},
|
||||||
gadgets::{
|
gadgets::r1cs::ConstraintSystem,
|
||||||
r1cs::ConstraintSystem,
|
|
||||||
utilities::{boolean::Boolean, select::CondSelectGadget},
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
pub fn check_arguments_length(expected: usize, actual: usize, span: Span) -> Result<(), FunctionError> {
|
||||||
fn check_arguments_length(expected: usize, actual: usize, span: Span) -> Result<(), FunctionError> {
|
// Make sure we are given the correct number of arguments
|
||||||
// Make sure we are given the correct number of arguments
|
if expected != actual {
|
||||||
if expected != actual {
|
Err(FunctionError::arguments_length(expected, actual, span))
|
||||||
Err(FunctionError::arguments_length(expected, actual, span))
|
} else {
|
||||||
} else {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn enforce_input<CS: ConstraintSystem<F>>(
|
|
||||||
&mut self,
|
|
||||||
cs: &mut CS,
|
|
||||||
scope: String,
|
|
||||||
caller_scope: String,
|
|
||||||
function_name: String,
|
|
||||||
expected_types: Vec<Type>,
|
|
||||||
input: Expression,
|
|
||||||
) -> Result<ConstrainedValue<F, G>, FunctionError> {
|
|
||||||
// Evaluate the function input value as pass by value from the caller or
|
|
||||||
// evaluate as an expression in the current function scope
|
|
||||||
match input {
|
|
||||||
Expression::Identifier(identifier) => {
|
|
||||||
Ok(self.evaluate_identifier(caller_scope, function_name, &expected_types, identifier)?)
|
|
||||||
}
|
|
||||||
expression => Ok(self.enforce_expression(cs, scope, function_name, &expected_types, expression)?),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// iterates through a vector of results and selects one based off of indicators
|
|
||||||
fn conditionally_select_result<CS: ConstraintSystem<F>>(
|
|
||||||
cs: &mut CS,
|
|
||||||
return_value: &mut ConstrainedValue<F, G>,
|
|
||||||
results: Vec<(Option<Boolean>, ConstrainedValue<F, G>)>,
|
|
||||||
span: Span,
|
|
||||||
) -> Result<(), StatementError> {
|
|
||||||
// if there are no results, continue
|
|
||||||
if results.len() == 0 {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
// If all indicators are none, then there are no branch conditions in the function.
|
|
||||||
// We simply return the last result.
|
|
||||||
|
|
||||||
if let None = results.iter().find(|(indicator, _res)| indicator.is_some()) {
|
|
||||||
let result = &results[results.len() - 1].1;
|
|
||||||
|
|
||||||
*return_value = result.clone();
|
|
||||||
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
// If there are branches in the function we need to use the `ConditionalSelectGadget` to parse through and select the correct one.
|
|
||||||
// This can be thought of as de-multiplexing all previous wires that may have returned results into one.
|
|
||||||
for (i, (indicator, result)) in results.into_iter().enumerate() {
|
|
||||||
// Set the first value as the starting point
|
|
||||||
if i == 0 {
|
|
||||||
*return_value = result.clone();
|
|
||||||
}
|
|
||||||
|
|
||||||
let condition = indicator.unwrap_or(Boolean::Constant(true));
|
|
||||||
let name_unique = format!("select {} {}:{}", result, span.line, span.start);
|
|
||||||
let selected_value =
|
|
||||||
ConstrainedValue::conditionally_select(cs.ns(|| name_unique), &condition, &result, return_value)
|
|
||||||
.map_err(|_| {
|
|
||||||
StatementError::select_fail(result.to_string(), return_value.to_string(), span.clone())
|
|
||||||
})?;
|
|
||||||
|
|
||||||
*return_value = selected_value;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||||
pub(crate) fn enforce_function<CS: ConstraintSystem<F>>(
|
pub(crate) fn enforce_function<CS: ConstraintSystem<F>>(
|
||||||
&mut self,
|
&mut self,
|
||||||
cs: &mut CS,
|
cs: &mut CS,
|
||||||
@ -109,7 +35,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
|||||||
let function_name = new_scope(scope.clone(), function.get_name());
|
let function_name = new_scope(scope.clone(), function.get_name());
|
||||||
|
|
||||||
// Make sure we are given the correct number of inputs
|
// Make sure we are given the correct number of inputs
|
||||||
Self::check_arguments_length(function.inputs.len(), inputs.len(), function.span.clone())?;
|
check_arguments_length(function.inputs.len(), inputs.len(), function.span.clone())?;
|
||||||
|
|
||||||
// Store input values as new variables in resolved program
|
// Store input values as new variables in resolved program
|
||||||
for (input_model, input_expression) in function.inputs.clone().iter().zip(inputs.into_iter()) {
|
for (input_model, input_expression) in function.inputs.clone().iter().zip(inputs.into_iter()) {
|
||||||
@ -133,7 +59,6 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Evaluate every statement in the function and save all potential results
|
// Evaluate every statement in the function and save all potential results
|
||||||
|
|
||||||
let mut results = vec![];
|
let mut results = vec![];
|
||||||
|
|
||||||
for statement in function.statements.iter() {
|
for statement in function.statements.iter() {
|
||||||
@ -166,114 +91,4 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
|||||||
|
|
||||||
Ok(return_values)
|
Ok(return_values)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn allocate_array<CS: ConstraintSystem<F>>(
|
|
||||||
&mut self,
|
|
||||||
cs: &mut CS,
|
|
||||||
name: String,
|
|
||||||
array_type: Type,
|
|
||||||
array_dimensions: Vec<usize>,
|
|
||||||
input_value: Option<InputValue>,
|
|
||||||
span: Span,
|
|
||||||
) -> Result<ConstrainedValue<F, G>, FunctionError> {
|
|
||||||
let expected_length = array_dimensions[0];
|
|
||||||
let mut array_value = vec![];
|
|
||||||
|
|
||||||
match input_value {
|
|
||||||
Some(InputValue::Array(arr)) => {
|
|
||||||
// Check the dimension of the array
|
|
||||||
Self::check_arguments_length(expected_length, arr.len(), span.clone())?;
|
|
||||||
|
|
||||||
// Allocate each value in the current row
|
|
||||||
for (i, value) in arr.into_iter().enumerate() {
|
|
||||||
let value_name = new_scope(name.clone(), i.to_string());
|
|
||||||
let value_type = array_type.outer_dimension(&array_dimensions);
|
|
||||||
|
|
||||||
array_value.push(self.allocate_main_function_input(
|
|
||||||
cs,
|
|
||||||
value_type,
|
|
||||||
value_name,
|
|
||||||
Some(value),
|
|
||||||
span.clone(),
|
|
||||||
)?)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
// Allocate all row values as none
|
|
||||||
for i in 0..expected_length {
|
|
||||||
let value_name = new_scope(name.clone(), i.to_string());
|
|
||||||
let value_type = array_type.outer_dimension(&array_dimensions);
|
|
||||||
|
|
||||||
array_value.push(self.allocate_main_function_input(
|
|
||||||
cs,
|
|
||||||
value_type,
|
|
||||||
value_name,
|
|
||||||
None,
|
|
||||||
span.clone(),
|
|
||||||
)?);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => return Err(FunctionError::invalid_array(input_value.unwrap().to_string(), span)),
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(ConstrainedValue::Array(array_value))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn allocate_main_function_input<CS: ConstraintSystem<F>>(
|
|
||||||
&mut self,
|
|
||||||
cs: &mut CS,
|
|
||||||
_type: Type,
|
|
||||||
name: String,
|
|
||||||
input_value: Option<InputValue>,
|
|
||||||
span: Span,
|
|
||||||
) -> Result<ConstrainedValue<F, G>, FunctionError> {
|
|
||||||
match _type {
|
|
||||||
Type::Address => Ok(Address::from_input(cs, name, input_value, span)?),
|
|
||||||
Type::Boolean => Ok(bool_from_input(cs, name, input_value, span)?),
|
|
||||||
Type::Field => Ok(field_from_input(cs, name, input_value, span)?),
|
|
||||||
Type::Group => Ok(group_from_input(cs, name, input_value, span)?),
|
|
||||||
Type::IntegerType(integer_type) => Ok(ConstrainedValue::Integer(Integer::from_input(
|
|
||||||
cs,
|
|
||||||
integer_type,
|
|
||||||
name,
|
|
||||||
input_value,
|
|
||||||
span,
|
|
||||||
)?)),
|
|
||||||
Type::Array(_type, dimensions) => self.allocate_array(cs, name, *_type, dimensions, input_value, span),
|
|
||||||
_ => unimplemented!("main function input not implemented for type"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn enforce_main_function<CS: ConstraintSystem<F>>(
|
|
||||||
&mut self,
|
|
||||||
cs: &mut CS,
|
|
||||||
scope: String,
|
|
||||||
function: Function,
|
|
||||||
inputs: Vec<Option<InputValue>>,
|
|
||||||
) -> Result<ConstrainedValue<F, G>, FunctionError> {
|
|
||||||
let function_name = new_scope(scope.clone(), function.get_name());
|
|
||||||
|
|
||||||
// Make sure we are given the correct number of inputs
|
|
||||||
Self::check_arguments_length(function.inputs.len(), inputs.len(), function.span.clone())?;
|
|
||||||
|
|
||||||
// Iterate over main function inputs and allocate new passed-by variable values
|
|
||||||
let mut input_variables = vec![];
|
|
||||||
for (input_model, input_option) in function.inputs.clone().into_iter().zip(inputs.into_iter()) {
|
|
||||||
let input_value = self.allocate_main_function_input(
|
|
||||||
cs,
|
|
||||||
input_model._type,
|
|
||||||
input_model.identifier.name.clone(),
|
|
||||||
input_option,
|
|
||||||
function.span.clone(),
|
|
||||||
)?;
|
|
||||||
|
|
||||||
// Store a new variable for every allocated main function input
|
|
||||||
let input_name = new_scope(function_name.clone(), input_model.identifier.name.clone());
|
|
||||||
self.store(input_name.clone(), input_value);
|
|
||||||
|
|
||||||
input_variables.push(Expression::Identifier(input_model.identifier));
|
|
||||||
}
|
|
||||||
|
|
||||||
self.enforce_function(cs, scope, function_name, function, input_variables)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
70
compiler/src/function/input/array.rs
Normal file
70
compiler/src/function/input/array.rs
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
//! Allocates an array as a main function input parameter in a compiled Leo program.
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
errors::FunctionError,
|
||||||
|
function::check_arguments_length,
|
||||||
|
program::{new_scope, ConstrainedProgram},
|
||||||
|
value::ConstrainedValue,
|
||||||
|
GroupType,
|
||||||
|
};
|
||||||
|
|
||||||
|
use leo_types::{InputValue, Span, Type};
|
||||||
|
|
||||||
|
use snarkos_models::{
|
||||||
|
curves::{Field, PrimeField},
|
||||||
|
gadgets::r1cs::ConstraintSystem,
|
||||||
|
};
|
||||||
|
|
||||||
|
impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||||
|
pub fn allocate_array<CS: ConstraintSystem<F>>(
|
||||||
|
&mut self,
|
||||||
|
cs: &mut CS,
|
||||||
|
name: String,
|
||||||
|
array_type: Type,
|
||||||
|
array_dimensions: Vec<usize>,
|
||||||
|
input_value: Option<InputValue>,
|
||||||
|
span: Span,
|
||||||
|
) -> Result<ConstrainedValue<F, G>, FunctionError> {
|
||||||
|
let expected_length = array_dimensions[0];
|
||||||
|
let mut array_value = vec![];
|
||||||
|
|
||||||
|
match input_value {
|
||||||
|
Some(InputValue::Array(arr)) => {
|
||||||
|
// Check the dimension of the array
|
||||||
|
check_arguments_length(expected_length, arr.len(), span.clone())?;
|
||||||
|
|
||||||
|
// Allocate each value in the current row
|
||||||
|
for (i, value) in arr.into_iter().enumerate() {
|
||||||
|
let value_name = new_scope(name.clone(), i.to_string());
|
||||||
|
let value_type = array_type.outer_dimension(&array_dimensions);
|
||||||
|
|
||||||
|
array_value.push(self.allocate_main_function_input(
|
||||||
|
cs,
|
||||||
|
value_type,
|
||||||
|
value_name,
|
||||||
|
Some(value),
|
||||||
|
span.clone(),
|
||||||
|
)?)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
// Allocate all row values as none
|
||||||
|
for i in 0..expected_length {
|
||||||
|
let value_name = new_scope(name.clone(), i.to_string());
|
||||||
|
let value_type = array_type.outer_dimension(&array_dimensions);
|
||||||
|
|
||||||
|
array_value.push(self.allocate_main_function_input(
|
||||||
|
cs,
|
||||||
|
value_type,
|
||||||
|
value_name,
|
||||||
|
None,
|
||||||
|
span.clone(),
|
||||||
|
)?);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => return Err(FunctionError::invalid_array(input_value.unwrap().to_string(), span)),
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(ConstrainedValue::Array(array_value))
|
||||||
|
}
|
||||||
|
}
|
31
compiler/src/function/input/input.rs
Normal file
31
compiler/src/function/input/input.rs
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
//! Enforces a function input parameter in a compiled Leo program.
|
||||||
|
|
||||||
|
use crate::{errors::FunctionError, program::ConstrainedProgram, value::ConstrainedValue, GroupType};
|
||||||
|
|
||||||
|
use leo_types::{Expression, Type};
|
||||||
|
|
||||||
|
use snarkos_models::{
|
||||||
|
curves::{Field, PrimeField},
|
||||||
|
gadgets::r1cs::ConstraintSystem,
|
||||||
|
};
|
||||||
|
|
||||||
|
impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||||
|
pub fn enforce_input<CS: ConstraintSystem<F>>(
|
||||||
|
&mut self,
|
||||||
|
cs: &mut CS,
|
||||||
|
scope: String,
|
||||||
|
caller_scope: String,
|
||||||
|
function_name: String,
|
||||||
|
expected_types: Vec<Type>,
|
||||||
|
input: Expression,
|
||||||
|
) -> Result<ConstrainedValue<F, G>, FunctionError> {
|
||||||
|
// Evaluate the function input value as pass by value from the caller or
|
||||||
|
// evaluate as an expression in the current function scope
|
||||||
|
match input {
|
||||||
|
Expression::Identifier(identifier) => {
|
||||||
|
Ok(self.evaluate_identifier(caller_scope, function_name, &expected_types, identifier)?)
|
||||||
|
}
|
||||||
|
expression => Ok(self.enforce_expression(cs, scope, function_name, &expected_types, expression)?),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
49
compiler/src/function/input/main_input.rs
Normal file
49
compiler/src/function/input/main_input.rs
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
//! Allocates a main function input parameter in a compiled Leo program.
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
address::Address,
|
||||||
|
errors::FunctionError,
|
||||||
|
program::ConstrainedProgram,
|
||||||
|
value::{
|
||||||
|
boolean::input::bool_from_input,
|
||||||
|
field::input::field_from_input,
|
||||||
|
group::input::group_from_input,
|
||||||
|
ConstrainedValue,
|
||||||
|
},
|
||||||
|
GroupType,
|
||||||
|
Integer,
|
||||||
|
};
|
||||||
|
|
||||||
|
use leo_types::{InputValue, Span, Type};
|
||||||
|
|
||||||
|
use snarkos_models::{
|
||||||
|
curves::{Field, PrimeField},
|
||||||
|
gadgets::r1cs::ConstraintSystem,
|
||||||
|
};
|
||||||
|
|
||||||
|
impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||||
|
pub fn allocate_main_function_input<CS: ConstraintSystem<F>>(
|
||||||
|
&mut self,
|
||||||
|
cs: &mut CS,
|
||||||
|
_type: Type,
|
||||||
|
name: String,
|
||||||
|
input_value: Option<InputValue>,
|
||||||
|
span: Span,
|
||||||
|
) -> Result<ConstrainedValue<F, G>, FunctionError> {
|
||||||
|
match _type {
|
||||||
|
Type::Address => Ok(Address::from_input(cs, name, input_value, span)?),
|
||||||
|
Type::Boolean => Ok(bool_from_input(cs, name, input_value, span)?),
|
||||||
|
Type::Field => Ok(field_from_input(cs, name, input_value, span)?),
|
||||||
|
Type::Group => Ok(group_from_input(cs, name, input_value, span)?),
|
||||||
|
Type::IntegerType(integer_type) => Ok(ConstrainedValue::Integer(Integer::from_input(
|
||||||
|
cs,
|
||||||
|
integer_type,
|
||||||
|
name,
|
||||||
|
input_value,
|
||||||
|
span,
|
||||||
|
)?)),
|
||||||
|
Type::Array(_type, dimensions) => self.allocate_array(cs, name, *_type, dimensions, input_value, span),
|
||||||
|
_ => unimplemented!("main function input not implemented for type"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
10
compiler/src/function/input/mod.rs
Normal file
10
compiler/src/function/input/mod.rs
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
//! Methods to enforce function inputs in a compiled Leo program.
|
||||||
|
|
||||||
|
pub mod array;
|
||||||
|
pub use self::array::*;
|
||||||
|
|
||||||
|
pub mod input;
|
||||||
|
pub use self::input::*;
|
||||||
|
|
||||||
|
pub mod main_input;
|
||||||
|
pub use self::main_input::*;
|
51
compiler/src/function/main_function.rs
Normal file
51
compiler/src/function/main_function.rs
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
//! Enforces constraints on the main function of a compiled Leo program.
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
errors::FunctionError,
|
||||||
|
function::check_arguments_length,
|
||||||
|
program::{new_scope, ConstrainedProgram},
|
||||||
|
value::ConstrainedValue,
|
||||||
|
GroupType,
|
||||||
|
};
|
||||||
|
|
||||||
|
use leo_types::{Expression, Function, InputValue};
|
||||||
|
|
||||||
|
use snarkos_models::{
|
||||||
|
curves::{Field, PrimeField},
|
||||||
|
gadgets::r1cs::ConstraintSystem,
|
||||||
|
};
|
||||||
|
|
||||||
|
impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||||
|
pub fn enforce_main_function<CS: ConstraintSystem<F>>(
|
||||||
|
&mut self,
|
||||||
|
cs: &mut CS,
|
||||||
|
scope: String,
|
||||||
|
function: Function,
|
||||||
|
inputs: Vec<Option<InputValue>>,
|
||||||
|
) -> Result<ConstrainedValue<F, G>, FunctionError> {
|
||||||
|
let function_name = new_scope(scope.clone(), function.get_name());
|
||||||
|
|
||||||
|
// Make sure we are given the correct number of inputs
|
||||||
|
check_arguments_length(function.inputs.len(), inputs.len(), function.span.clone())?;
|
||||||
|
|
||||||
|
// Iterate over main function inputs and allocate new passed-by variable values
|
||||||
|
let mut input_variables = vec![];
|
||||||
|
for (input_model, input_option) in function.inputs.clone().into_iter().zip(inputs.into_iter()) {
|
||||||
|
let input_value = self.allocate_main_function_input(
|
||||||
|
cs,
|
||||||
|
input_model._type,
|
||||||
|
input_model.identifier.name.clone(),
|
||||||
|
input_option,
|
||||||
|
function.span.clone(),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
// Store a new variable for every allocated main function input
|
||||||
|
let input_name = new_scope(function_name.clone(), input_model.identifier.name.clone());
|
||||||
|
self.store(input_name.clone(), input_value);
|
||||||
|
|
||||||
|
input_variables.push(Expression::Identifier(input_model.identifier));
|
||||||
|
}
|
||||||
|
|
||||||
|
self.enforce_function(cs, scope, function_name, function, input_variables)
|
||||||
|
}
|
||||||
|
}
|
@ -1,2 +1,13 @@
|
|||||||
|
//! Methods to enforce constraints on functions in a compiled Leo program.
|
||||||
|
|
||||||
|
pub mod input;
|
||||||
|
pub use self::input::*;
|
||||||
|
|
||||||
pub mod function;
|
pub mod function;
|
||||||
pub use self::function::*;
|
pub use self::function::*;
|
||||||
|
|
||||||
|
pub mod main_function;
|
||||||
|
pub use self::main_function::*;
|
||||||
|
|
||||||
|
pub mod result;
|
||||||
|
pub use self::result::*;
|
||||||
|
4
compiler/src/function/result/mod.rs
Normal file
4
compiler/src/function/result/mod.rs
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
//! Methods to enforce constraints on a function result in a compiled Leo program.
|
||||||
|
|
||||||
|
pub mod result;
|
||||||
|
pub use self::result::*;
|
60
compiler/src/function/result/result.rs
Normal file
60
compiler/src/function/result/result.rs
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
//! Enforces that one return value is produced in a compiled Leo program.
|
||||||
|
|
||||||
|
use crate::{errors::StatementError, program::ConstrainedProgram, value::ConstrainedValue, GroupType};
|
||||||
|
|
||||||
|
use leo_types::Span;
|
||||||
|
|
||||||
|
use snarkos_models::{
|
||||||
|
curves::{Field, PrimeField},
|
||||||
|
gadgets::{
|
||||||
|
r1cs::ConstraintSystem,
|
||||||
|
utilities::{boolean::Boolean, select::CondSelectGadget},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||||
|
/// iterates through a vector of results and selects one based off of indicators
|
||||||
|
pub fn conditionally_select_result<CS: ConstraintSystem<F>>(
|
||||||
|
cs: &mut CS,
|
||||||
|
return_value: &mut ConstrainedValue<F, G>,
|
||||||
|
results: Vec<(Option<Boolean>, ConstrainedValue<F, G>)>,
|
||||||
|
span: Span,
|
||||||
|
) -> Result<(), StatementError> {
|
||||||
|
// if there are no results, continue
|
||||||
|
if results.len() == 0 {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
// If all indicators are none, then there are no branch conditions in the function.
|
||||||
|
// We simply return the last result.
|
||||||
|
|
||||||
|
if let None = results.iter().find(|(indicator, _res)| indicator.is_some()) {
|
||||||
|
let result = &results[results.len() - 1].1;
|
||||||
|
|
||||||
|
*return_value = result.clone();
|
||||||
|
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there are branches in the function we need to use the `ConditionalSelectGadget` to parse through and select the correct one.
|
||||||
|
// This can be thought of as de-multiplexing all previous wires that may have returned results into one.
|
||||||
|
for (i, (indicator, result)) in results.into_iter().enumerate() {
|
||||||
|
// Set the first value as the starting point
|
||||||
|
if i == 0 {
|
||||||
|
*return_value = result.clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
let condition = indicator.unwrap_or(Boolean::Constant(true));
|
||||||
|
let name_unique = format!("select {} {}:{}", result, span.line, span.start);
|
||||||
|
let selected_value =
|
||||||
|
ConstrainedValue::conditionally_select(cs.ns(|| name_unique), &condition, &result, return_value)
|
||||||
|
.map_err(|_| {
|
||||||
|
StatementError::select_fail(result.to_string(), return_value.to_string(), span.clone())
|
||||||
|
})?;
|
||||||
|
|
||||||
|
*return_value = selected_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
//! Methods to enforce constraints on statements in a compiled Leo program.
|
//! Enforces a statement in a compiled Leo program.
|
||||||
|
|
||||||
use crate::{errors::StatementError, program::ConstrainedProgram, value::ConstrainedValue, GroupType};
|
use crate::{errors::StatementError, program::ConstrainedProgram, value::ConstrainedValue, GroupType};
|
||||||
use leo_types::{Statement, Type};
|
use leo_types::{Statement, Type};
|
||||||
@ -12,7 +12,8 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
|||||||
/// Enforce a program statement.
|
/// Enforce a program statement.
|
||||||
/// Returns a Vector of (indicator, value) tuples.
|
/// Returns a Vector of (indicator, value) tuples.
|
||||||
/// Each evaluated statement may execute of one or more statements that may return early.
|
/// Each evaluated statement may execute of one or more statements that may return early.
|
||||||
/// To indicate which of these return values to take we conditionally select that value with the indicator bit.
|
/// To indicate which of these return values to take,
|
||||||
|
/// we conditionally select the value according the `indicator` bit that evaluates to true.
|
||||||
pub fn enforce_statement<CS: ConstraintSystem<F>>(
|
pub fn enforce_statement<CS: ConstraintSystem<F>>(
|
||||||
&mut self,
|
&mut self,
|
||||||
cs: &mut CS,
|
cs: &mut CS,
|
||||||
@ -23,6 +24,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
|||||||
return_types: Vec<Type>,
|
return_types: Vec<Type>,
|
||||||
) -> Result<Vec<(Option<Boolean>, ConstrainedValue<F, G>)>, StatementError> {
|
) -> Result<Vec<(Option<Boolean>, ConstrainedValue<F, G>)>, StatementError> {
|
||||||
let mut results = vec![];
|
let mut results = vec![];
|
||||||
|
|
||||||
match statement {
|
match statement {
|
||||||
Statement::Return(expressions, span) => {
|
Statement::Return(expressions, span) => {
|
||||||
let return_value = (
|
let return_value = (
|
||||||
|
Loading…
Reference in New Issue
Block a user